Parcelable: Add Parcelable functionality to Android Extensions plugin
This commit is contained in:
+144
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.synthetic.codegen
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.android.parcel.isMagicParcelable
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactory
|
||||
import org.jetbrains.kotlin.codegen.DelegatingClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class ParcelableClinitClassBuilderInterceptorExtension : ClassBuilderInterceptorExtension {
|
||||
override fun interceptClassBuilderFactory(
|
||||
interceptedFactory: ClassBuilderFactory,
|
||||
bindingContext: BindingContext,
|
||||
diagnostics: DiagnosticSink
|
||||
): ClassBuilderFactory {
|
||||
return ParcelableClinitClassBuilderFactory(interceptedFactory, bindingContext)
|
||||
}
|
||||
|
||||
private inner class ParcelableClinitClassBuilderFactory(
|
||||
private val delegateFactory: ClassBuilderFactory,
|
||||
val bindingContext: BindingContext
|
||||
) : ClassBuilderFactory {
|
||||
|
||||
override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder {
|
||||
return AndroidOnDestroyCollectorClassBuilder(delegateFactory.newClassBuilder(origin), bindingContext)
|
||||
}
|
||||
|
||||
override fun getClassBuilderMode() = delegateFactory.classBuilderMode
|
||||
|
||||
override fun asText(builder: ClassBuilder?): String? {
|
||||
return delegateFactory.asText((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder)
|
||||
}
|
||||
|
||||
override fun asBytes(builder: ClassBuilder?): ByteArray? {
|
||||
return delegateFactory.asBytes((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
delegateFactory.close()
|
||||
}
|
||||
}
|
||||
|
||||
private inner class AndroidOnDestroyCollectorClassBuilder(
|
||||
internal val delegateClassBuilder: ClassBuilder,
|
||||
val bindingContext: BindingContext
|
||||
) : DelegatingClassBuilder() {
|
||||
private var currentClass: KtClass? = null
|
||||
private var currentClassName: String? = null
|
||||
private var isClinitGenerated = false
|
||||
|
||||
override fun getDelegate() = delegateClassBuilder
|
||||
|
||||
override fun defineClass(
|
||||
origin: PsiElement?,
|
||||
version: Int,
|
||||
access: Int,
|
||||
name: String,
|
||||
signature: String?,
|
||||
superName: String,
|
||||
interfaces: Array<out String>
|
||||
) {
|
||||
if (origin is KtClass) {
|
||||
currentClass = origin
|
||||
}
|
||||
|
||||
currentClassName = name
|
||||
isClinitGenerated = false
|
||||
|
||||
super.defineClass(origin, version, access, name, signature, superName, interfaces)
|
||||
}
|
||||
|
||||
override fun done() {
|
||||
if (!isClinitGenerated && currentClass != null && currentClassName != null) {
|
||||
val descriptor = bindingContext[BindingContext.CLASS, currentClass]
|
||||
if (descriptor != null && descriptor.isMagicParcelable) {
|
||||
val baseVisitor = super.newMethod(JvmDeclarationOrigin.NO_ORIGIN, ACC_STATIC, "<clinit>", "()V", null, null)
|
||||
val visitor = ClinitAwareMethodVisitor(currentClassName!!, baseVisitor)
|
||||
|
||||
visitor.visitCode()
|
||||
visitor.visitInsn(Opcodes.RETURN)
|
||||
visitor.visitEnd()
|
||||
}
|
||||
}
|
||||
|
||||
super.done()
|
||||
}
|
||||
|
||||
override fun newMethod(
|
||||
origin: JvmDeclarationOrigin,
|
||||
access: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor {
|
||||
if (name == "<clinit>" && currentClass != null && currentClassName != null) {
|
||||
isClinitGenerated = true
|
||||
return ClinitAwareMethodVisitor(currentClassName!!, super.newMethod(origin, access, name, desc, signature, exceptions))
|
||||
}
|
||||
|
||||
return super.newMethod(origin, access, name, desc, signature, exceptions)
|
||||
}
|
||||
}
|
||||
|
||||
private class ClinitAwareMethodVisitor(val parcelableName: String, mv: MethodVisitor) : MethodVisitor(Opcodes.ASM5, mv) {
|
||||
override fun visitInsn(opcode: Int) {
|
||||
if (opcode == Opcodes.RETURN) {
|
||||
val iv = InstructionAdapter(this)
|
||||
val creatorName = "$parcelableName\$CREATOR"
|
||||
val creatorType = Type.getObjectType(creatorName)
|
||||
|
||||
iv.anew(creatorType)
|
||||
iv.dup()
|
||||
iv.invokespecial(creatorName, "<init>", "()V", false)
|
||||
iv.putstatic(parcelableName, "CREATOR", creatorType.descriptor)
|
||||
}
|
||||
|
||||
super.visitInsn(opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.parcel
|
||||
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableSyntheticComponent.ComponentKind.*
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableResolveExtension.Companion.createMethod
|
||||
import org.jetbrains.kotlin.android.parcel.serializers.ParcelSerializer
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
|
||||
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.FunctionGenerationStrategy.CodegenBased
|
||||
import org.jetbrains.kotlin.codegen.OwnerKind
|
||||
import org.jetbrains.kotlin.codegen.context.ClassContext
|
||||
import org.jetbrains.kotlin.codegen.writeSyntheticClassMetadata
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.DescriptorFactory
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import java.io.FileDescriptor
|
||||
|
||||
class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
||||
private companion object {
|
||||
private val FILE_DESCRIPTOR_FQNAME = FqName(FileDescriptor::class.java.canonicalName)
|
||||
}
|
||||
|
||||
override fun generateClassSyntheticParts(codegen: ImplementationBodyCodegen) {
|
||||
val parcelableClass = codegen.descriptor
|
||||
if (!parcelableClass.isMagicParcelable) return
|
||||
assert(parcelableClass.kind == ClassKind.CLASS || parcelableClass.kind == ClassKind.OBJECT)
|
||||
|
||||
val propertiesToSerialize = getPropertiesToSerialize(codegen, parcelableClass)
|
||||
|
||||
val parcelClassType = ParcelableResolveExtension.resolveParcelClassType(parcelableClass.module)
|
||||
val parcelAsmType = codegen.typeMapper.mapType(parcelClassType)
|
||||
|
||||
with (parcelableClass) {
|
||||
writeDescribeContentsFunction(codegen, propertiesToSerialize)
|
||||
writeWriteToParcel(codegen, propertiesToSerialize, parcelAsmType)
|
||||
}
|
||||
|
||||
writeCreatorAccessField(codegen, parcelableClass)
|
||||
writeCreatorClass(codegen, parcelableClass, parcelClassType, parcelAsmType, propertiesToSerialize)
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.writeWriteToParcel(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
properties: List<Pair<String, KotlinType>>,
|
||||
parcelAsmType: Type
|
||||
): Unit? {
|
||||
val containerAsmType = codegen.typeMapper.mapType(this.defaultType)
|
||||
|
||||
return findFunction(WRITE_TO_PARCEL)?.write(codegen) {
|
||||
for ((fieldName, type) in properties) {
|
||||
val asmType = codegen.typeMapper.mapType(type)
|
||||
|
||||
v.load(1, parcelAsmType)
|
||||
v.load(0, containerAsmType)
|
||||
v.getfield(containerAsmType.internalName, fieldName, asmType.descriptor)
|
||||
|
||||
val serializer = ParcelSerializer.get(type, asmType, codegen.typeMapper)
|
||||
serializer.writeValue(v)
|
||||
}
|
||||
|
||||
v.areturn(Type.VOID_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.writeDescribeContentsFunction(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
propertiesToSerialize: List<Pair<String, KotlinType>>
|
||||
): Unit? {
|
||||
val hasFileDescriptorAnywhere = propertiesToSerialize.any { it.second.containsFileDescriptor() }
|
||||
|
||||
return findFunction(DESCRIBE_CONTENTS)?.write(codegen) {
|
||||
v.aconst(if (hasFileDescriptorAnywhere) 1 /* CONTENTS_FILE_DESCRIPTOR */ else 0)
|
||||
v.areturn(Type.INT_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinType.containsFileDescriptor(): Boolean {
|
||||
val declarationDescriptor = this.constructor.declarationDescriptor
|
||||
if (declarationDescriptor != null) {
|
||||
if (declarationDescriptor.fqNameSafe == FILE_DESCRIPTOR_FQNAME) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return this.arguments.any { it.type.containsFileDescriptor() }
|
||||
}
|
||||
|
||||
private fun getPropertiesToSerialize(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
parcelableClass: ClassDescriptor
|
||||
): List<Pair<String, KotlinType>> {
|
||||
val constructor = parcelableClass.constructors.first { it.isPrimary }
|
||||
|
||||
val propertiesToSerialize = constructor.valueParameters.map { param ->
|
||||
codegen.bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, param]
|
||||
?: error("Value parameter should have 'val' or 'var' keyword")
|
||||
}
|
||||
|
||||
return propertiesToSerialize.map { it.name.asString() /* TODO */ to it.type }
|
||||
}
|
||||
|
||||
private fun writeCreateFromParcel(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
parcelableClass: ClassDescriptor,
|
||||
creatorClass: ClassDescriptorImpl,
|
||||
parcelClassType: KotlinType,
|
||||
parcelAsmType: Type,
|
||||
properties: List<Pair<String, KotlinType>>
|
||||
) {
|
||||
val containerAsmType = codegen.typeMapper.mapType(parcelableClass)
|
||||
|
||||
createMethod(creatorClass, CREATE_FROM_PARCEL, parcelableClass.defaultType, "in" to parcelClassType).write(codegen) {
|
||||
v.anew(containerAsmType)
|
||||
v.dup()
|
||||
|
||||
val asmConstructorParameters = StringBuilder()
|
||||
|
||||
for ((_, type) in properties) {
|
||||
val asmType = codegen.typeMapper.mapType(type)
|
||||
asmConstructorParameters.append(asmType.descriptor)
|
||||
|
||||
val serializer = ParcelSerializer.get(type, asmType, codegen.typeMapper)
|
||||
v.load(1, parcelAsmType)
|
||||
serializer.readValue(v)
|
||||
}
|
||||
|
||||
v.invokespecial(containerAsmType.internalName, "<init>", "($asmConstructorParameters)V", false)
|
||||
v.areturn(containerAsmType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeCreatorAccessField(codegen: ImplementationBodyCodegen, parcelableClass: ClassDescriptor) {
|
||||
val parcelableAsmType = codegen.typeMapper.mapType(parcelableClass.defaultType)
|
||||
val creatorAsmType = Type.getObjectType(parcelableAsmType.internalName + "\$CREATOR")
|
||||
codegen.v.newField(JvmDeclarationOrigin.NO_ORIGIN, ACC_STATIC or ACC_PUBLIC or ACC_FINAL, "CREATOR",
|
||||
creatorAsmType.descriptor, null, null)
|
||||
}
|
||||
|
||||
private fun writeCreatorClass(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
parcelableClass: ClassDescriptor,
|
||||
parcelClassType: KotlinType,
|
||||
parcelAsmType: Type,
|
||||
properties: List<Pair<String, KotlinType>>
|
||||
) {
|
||||
val containerAsmType = codegen.typeMapper.mapType(parcelableClass.defaultType)
|
||||
val creatorAsmType = Type.getObjectType(containerAsmType.internalName + "\$CREATOR")
|
||||
|
||||
val creatorClass = ClassDescriptorImpl(
|
||||
parcelableClass.containingDeclaration, Name.identifier("Creator"), Modality.FINAL, ClassKind.CLASS, emptyList(),
|
||||
parcelableClass.source, false)
|
||||
|
||||
creatorClass.initialize(
|
||||
MemberScope.Empty, emptySet(),
|
||||
DescriptorFactory.createPrimaryConstructorForObject(creatorClass, creatorClass.source))
|
||||
|
||||
val classBuilderForCreator = codegen.state.factory.newVisitor(
|
||||
JvmDeclarationOrigin.NO_ORIGIN,
|
||||
Type.getObjectType(creatorAsmType.internalName),
|
||||
codegen.myClass.containingKtFile)
|
||||
|
||||
val classContextForCreator = ClassContext(
|
||||
codegen.typeMapper, creatorClass, OwnerKind.IMPLEMENTATION, codegen.context.parentContext, null)
|
||||
val codegenForCreator = ImplementationBodyCodegen(
|
||||
codegen.myClass, classContextForCreator, classBuilderForCreator, codegen.state, codegen.parentCodegen, false)
|
||||
|
||||
classBuilderForCreator.defineClass(null, V1_6, ACC_PUBLIC or ACC_STATIC,
|
||||
creatorAsmType.internalName, null, "java/lang/Object",
|
||||
arrayOf("android/os/Parcelable\$Creator"))
|
||||
|
||||
writeSyntheticClassMetadata(classBuilderForCreator, codegen.state)
|
||||
|
||||
writeCreatorConstructor(codegenForCreator, creatorClass, creatorAsmType)
|
||||
writeNewArrayMethod(codegenForCreator, parcelableClass, creatorClass)
|
||||
writeCreateFromParcel(codegenForCreator, parcelableClass, creatorClass, parcelClassType, parcelAsmType, properties)
|
||||
|
||||
classBuilderForCreator.done()
|
||||
}
|
||||
|
||||
private fun writeCreatorConstructor(codegen: ImplementationBodyCodegen, creatorClass: ClassDescriptor, creatorAsmType: Type) {
|
||||
DescriptorFactory.createPrimaryConstructorForObject(creatorClass, creatorClass.source)
|
||||
.apply {
|
||||
returnType = creatorClass.defaultType
|
||||
}.write(codegen) {
|
||||
v.load(0, creatorAsmType)
|
||||
v.invokespecial("java/lang/Object", "<init>", "()V", false)
|
||||
v.areturn(Type.VOID_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeNewArrayMethod(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
parcelableClass: ClassDescriptor,
|
||||
creatorClass: ClassDescriptorImpl
|
||||
) {
|
||||
val builtIns = parcelableClass.builtIns
|
||||
val parcelableAsmType = codegen.typeMapper.mapType(parcelableClass)
|
||||
|
||||
createMethod(creatorClass, NEW_ARRAY,
|
||||
builtIns.getArrayType(Variance.INVARIANT, parcelableClass.defaultType),
|
||||
"size" to builtIns.intType
|
||||
).write(codegen) {
|
||||
v.load(1, Type.INT_TYPE)
|
||||
v.newarray(parcelableAsmType)
|
||||
v.areturn(Type.getType("[L$parcelableAsmType;"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.write(codegen: ImplementationBodyCodegen, code: ExpressionCodegen.() -> Unit) {
|
||||
codegen.functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, this, object : CodegenBased(codegen.state) {
|
||||
override fun doGenerateBody(e: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
e.code()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.findFunction(componentKind: ParcelableSyntheticComponent.ComponentKind): SimpleFunctionDescriptor? {
|
||||
return unsubstitutedMemberScope
|
||||
.getContributedFunctions(Name.identifier(componentKind.methodName), WHEN_GET_ALL_DESCRIPTORS)
|
||||
.firstOrNull { (it as? ParcelableSyntheticComponent)?.componentKind == componentKind }
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.parcel
|
||||
|
||||
import kotlinx.android.parcel.MagicParcel
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableSyntheticComponent.ComponentKind.*
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
|
||||
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 createMethod(
|
||||
classDescriptor: ClassDescriptor,
|
||||
componentKind: ParcelableSyntheticComponent.ComponentKind,
|
||||
returnType: KotlinType,
|
||||
vararg parameters: Pair<String, KotlinType>
|
||||
): SimpleFunctionDescriptor {
|
||||
val functionDescriptor = object : ParcelableSyntheticComponent, SimpleFunctionDescriptorImpl(
|
||||
classDescriptor,
|
||||
null,
|
||||
Annotations.EMPTY,
|
||||
Name.identifier(componentKind.methodName),
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
classDescriptor.source
|
||||
) {
|
||||
override val componentKind = componentKind
|
||||
}
|
||||
|
||||
val valueParameters = parameters.mapIndexed { index, (name, type) -> functionDescriptor.makeValueParameter(name, type, index) }
|
||||
|
||||
functionDescriptor.initialize(
|
||||
null, classDescriptor.thisAsReceiverParameter, emptyList(), valueParameters,
|
||||
returnType, Modality.FINAL, Visibilities.PUBLIC)
|
||||
|
||||
return functionDescriptor
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.makeValueParameter(name: String, type: KotlinType, index: Int): ValueParameterDescriptor {
|
||||
return ValueParameterDescriptorImpl(
|
||||
this, null, index, Annotations.EMPTY, Name.identifier(name), type, false, false, false, null, this.source)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSyntheticCompanionObjectNameIfNeeded(thisDescriptor: ClassDescriptor) = null
|
||||
|
||||
override fun generateSyntheticMethods(
|
||||
clazz: ClassDescriptor,
|
||||
name: Name,
|
||||
fromSupertypes: List<SimpleFunctionDescriptor>,
|
||||
result: MutableCollection<SimpleFunctionDescriptor>
|
||||
) {
|
||||
if (name.asString() == DESCRIBE_CONTENTS.methodName && clazz.isMagicParcelable) {
|
||||
result += createMethod(clazz, DESCRIBE_CONTENTS, clazz.builtIns.intType)
|
||||
} else if (name.asString() == WRITE_TO_PARCEL.methodName && clazz.isMagicParcelable) {
|
||||
val builtIns = clazz.builtIns
|
||||
val parcelClassType = resolveParcelClassType(clazz.module)
|
||||
result += createMethod(clazz, WRITE_TO_PARCEL, builtIns.unitType, "parcel" to parcelClassType, "flags" to builtIns.intType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ParcelableSyntheticComponent {
|
||||
val componentKind: ComponentKind
|
||||
|
||||
enum class ComponentKind(val methodName: String) {
|
||||
WRITE_TO_PARCEL("writeToParcel"),
|
||||
DESCRIBE_CONTENTS("describeContents"),
|
||||
NEW_ARRAY("newArray"),
|
||||
CREATE_FROM_PARCEL("createFromParcel")
|
||||
}
|
||||
}
|
||||
|
||||
private val MAGIC_PARCEL_CLASS_FQNAME = FqName(MagicParcel::class.java.canonicalName)
|
||||
|
||||
internal val ClassDescriptor.isMagicParcelable: Boolean
|
||||
get() = this.annotations.hasAnnotation(MAGIC_PARCEL_CLASS_FQNAME)
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.parcel.serializers
|
||||
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
interface ParcelSerializer {
|
||||
val asmType: Type
|
||||
|
||||
fun writeValue(v: InstructionAdapter)
|
||||
fun readValue(v: InstructionAdapter)
|
||||
|
||||
companion object {
|
||||
fun get(type: KotlinType, asmType: Type, typeMapper: KotlinTypeMapper, forceBoxed: Boolean = false): ParcelSerializer {
|
||||
val className = asmType.className
|
||||
|
||||
return when {
|
||||
asmType.sort == Type.ARRAY -> {
|
||||
val elementType = type.builtIns.getArrayElementType(type)
|
||||
|
||||
wrapToNullAwareIfNeeded(
|
||||
type,
|
||||
ArrayParcelSerializer(asmType, get(elementType, typeMapper.mapType(elementType), typeMapper)))
|
||||
}
|
||||
|
||||
asmType.isPrimitive() -> {
|
||||
if (forceBoxed || type.isMarkedNullable)
|
||||
wrapToNullAwareIfNeeded(type, BoxedPrimitiveTypeParcelSerializer.forUnboxedType(asmType))
|
||||
else
|
||||
PrimitiveTypeParcelSerializer.getInstance(asmType)
|
||||
}
|
||||
|
||||
asmType.isString() -> NullCompliantObjectParcelSerializer(asmType,
|
||||
Method("writeString"),
|
||||
Method("readString"))
|
||||
|
||||
className == List::class.java.canonicalName
|
||||
|| className == ArrayList::class.java.canonicalName
|
||||
|| className == LinkedList::class.java.canonicalName
|
||||
|| className == Set::class.java.canonicalName
|
||||
|| className == HashSet::class.java.canonicalName
|
||||
|| className == LinkedHashSet::class.java.canonicalName
|
||||
|| className == TreeSet::class.java.canonicalName
|
||||
-> {
|
||||
val elementType = type.arguments.single().type
|
||||
val elementSerializer = get(elementType, typeMapper.mapType(elementType), typeMapper, forceBoxed = true)
|
||||
wrapToNullAwareIfNeeded(type, ListSetParcelSerializer(asmType, elementSerializer))
|
||||
}
|
||||
|
||||
className == Map::class.java.canonicalName
|
||||
|| className == HashMap::class.java.canonicalName
|
||||
|| className == LinkedHashMap::class.java.canonicalName
|
||||
|| className == TreeMap::class.java.canonicalName
|
||||
|| className == ConcurrentHashMap::class.java.canonicalName
|
||||
-> {
|
||||
val (keyType, valueType) = type.arguments.apply { assert(this.size == 2) }
|
||||
val keySerializer = get(keyType.type, typeMapper.mapType(keyType.type), typeMapper, forceBoxed = true)
|
||||
val valueSerializer = get(valueType.type, typeMapper.mapType(valueType.type), typeMapper, forceBoxed = true)
|
||||
wrapToNullAwareIfNeeded(type, MapParcelSerializer(asmType, keySerializer, valueSerializer))
|
||||
}
|
||||
|
||||
asmType.isBoxedPrimitive() -> wrapToNullAwareIfNeeded(type, BoxedPrimitiveTypeParcelSerializer.forBoxedType(asmType))
|
||||
|
||||
asmType.isBlob() -> NullCompliantObjectParcelSerializer(asmType,
|
||||
Method("writeBlob"),
|
||||
Method("readBlob"))
|
||||
|
||||
asmType.isSize() -> wrapToNullAwareIfNeeded(type, NullCompliantObjectParcelSerializer(asmType,
|
||||
Method("writeSize"),
|
||||
Method("readSize")))
|
||||
|
||||
asmType.isSizeF() -> wrapToNullAwareIfNeeded(type, NullCompliantObjectParcelSerializer(asmType,
|
||||
Method("writeSizeF"),
|
||||
Method("readSizeF")))
|
||||
|
||||
asmType.isBundle() -> NullCompliantObjectParcelSerializer(asmType,
|
||||
Method("writeBundle"),
|
||||
Method("readBundle"))
|
||||
|
||||
asmType.isPersistableBundle() -> NullCompliantObjectParcelSerializer(asmType,
|
||||
Method("writeBundle"),
|
||||
Method("readBundle"))
|
||||
|
||||
asmType.isSparseBooleanArray() -> NullCompliantObjectParcelSerializer(asmType,
|
||||
Method("writeSparseBooleanArray"),
|
||||
Method("readSparseBooleanArray"))
|
||||
|
||||
asmType.isSparseIntArray() -> wrapToNullAwareIfNeeded(type, SparseArrayParcelSerializer(
|
||||
asmType, PrimitiveTypeParcelSerializer.getInstance(Type.INT_TYPE)))
|
||||
|
||||
asmType.isSparseLongArray() -> wrapToNullAwareIfNeeded(type, SparseArrayParcelSerializer(
|
||||
asmType, PrimitiveTypeParcelSerializer.getInstance(Type.LONG_TYPE)))
|
||||
|
||||
asmType.isSparseArray() -> {
|
||||
val elementType = type.arguments.single().type
|
||||
val elementSerializer = get(elementType, typeMapper.mapType(elementType), typeMapper, forceBoxed = true)
|
||||
wrapToNullAwareIfNeeded(type, SparseArrayParcelSerializer(asmType, elementSerializer))
|
||||
}
|
||||
|
||||
type.isException() -> wrapToNullAwareIfNeeded(type, NullCompliantObjectParcelSerializer(asmType,
|
||||
Method("writeException"),
|
||||
Method("readException")))
|
||||
|
||||
asmType.isFileDescriptor() -> wrapToNullAwareIfNeeded(type, NullCompliantObjectParcelSerializer(asmType,
|
||||
Method("writeRawFileDescriptor"),
|
||||
Method("readRawFileDescriptor")))
|
||||
|
||||
type.isSerializable() -> NullCompliantObjectParcelSerializer(asmType,
|
||||
Method("writeSerializable"),
|
||||
Method("readSerializable"))
|
||||
|
||||
else -> GenericParcelSerializer
|
||||
}
|
||||
}
|
||||
private fun wrapToNullAwareIfNeeded(type: KotlinType, serializer: ParcelSerializer) = when {
|
||||
type.isMarkedNullable -> NullAwareParcelSerializerWrapper(serializer)
|
||||
else -> serializer
|
||||
}
|
||||
|
||||
private fun Type.isBlob() = this.sort == Type.ARRAY && this.elementType == Type.BYTE_TYPE
|
||||
private fun Type.isString() = this.descriptor == "Ljava/lang/String;"
|
||||
private fun Type.isSize() = this.descriptor == "Landroid/util/Size;"
|
||||
private fun Type.isSizeF() = this.descriptor == "Landroid/util/SizeF;"
|
||||
private fun Type.isFileDescriptor() = this.descriptor == "Ljava/io/FileDescriptor;"
|
||||
private fun Type.isBundle() = this.descriptor == "Landroid/os/Bundle;"
|
||||
private fun Type.isPersistableBundle() = this.descriptor == "Landroid/os/PersistableBundle;"
|
||||
private fun Type.isSparseBooleanArray() = this.descriptor == "Landroid/util/SparseBooleanArray;"
|
||||
private fun Type.isSparseIntArray() = this.descriptor == "Landroid/util/SparseIntArray;"
|
||||
private fun Type.isSparseLongArray() = this.descriptor == "Landroid/util/SparseLongArray;"
|
||||
private fun Type.isSparseArray() = this.descriptor == "Landroid/util/SparseArray;"
|
||||
private fun KotlinType.isSerializable() = matchesFqNameWithSupertypes("java.io.Serializable")
|
||||
private fun KotlinType.isException() = matchesFqNameWithSupertypes("java.lang.Exception")
|
||||
|
||||
private fun Type.isPrimitive(): Boolean = when (this.sort) {
|
||||
Type.BOOLEAN, Type.CHAR, Type.BYTE, Type.SHORT, Type.INT, Type.FLOAT, Type.LONG, Type.DOUBLE -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun Type.isBoxedPrimitive(): Boolean = when(this.descriptor) {
|
||||
"Ljava/lang/Boolean;",
|
||||
"Ljava/lang/Character;",
|
||||
"Ljava/lang/Byte;",
|
||||
"Ljava/lang/Short;",
|
||||
"Ljava/lang/Integer;",
|
||||
"Ljava/lang/Float;",
|
||||
"Ljava/lang/Long;",
|
||||
"Ljava/lang/Double;" -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun KotlinType.matchesFqNameWithSupertypes(fqName: String): Boolean {
|
||||
if (this.matchesFqName(fqName)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return this.constructor.supertypes.any { it.matchesFqName(fqName) }
|
||||
}
|
||||
|
||||
private fun KotlinType.matchesFqName(fqName: String): Boolean {
|
||||
return this.constructor.declarationDescriptor?.fqNameSafe?.asString() == fqName
|
||||
}
|
||||
}
|
||||
}
|
||||
+579
@@ -0,0 +1,579 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.parcel.serializers
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
private val PARCEL_TYPE = Type.getObjectType("android/os/Parcel")
|
||||
|
||||
internal object GenericParcelSerializer : ParcelSerializer {
|
||||
override val asmType: Type = Type.getObjectType("java/lang/Object")
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeValue", "(Ljava/lang/Object;)V", false)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.aconst(asmType) // -> parcel, type
|
||||
v.invokevirtual("java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false) // -> parcel, classloader
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readValue", "(Ljava/lang/ClassLoader;)Ljava/lang/Object;", false)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ArrayParcelSerializer(override val asmType: Type, private val elementSerializer: ParcelSerializer) : ParcelSerializer {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.dupX1() // -> arr, parcel, arr
|
||||
v.arraylength() // -> arr, parcel, length
|
||||
v.dupX2() // -> length, arr, parcel, length
|
||||
|
||||
// Write array size
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false) // -> length, arr
|
||||
v.swap() // -> arr, length
|
||||
v.aconst(0) // -> arr, length, <index>
|
||||
|
||||
val nextLoopIteration = Label()
|
||||
val loopIsOver = Label()
|
||||
|
||||
v.visitLabel(nextLoopIteration)
|
||||
|
||||
// Loop
|
||||
v.dup2() // -> arr, length, index, length, index
|
||||
v.ificmple(loopIsOver) // -> arr, length, index
|
||||
|
||||
v.swap() // -> arr, index, length
|
||||
v.dupX2() // -> length, arr, index, length
|
||||
v.pop() // -> length, arr, index
|
||||
v.dup2() // -> length, arr, index, arr, index
|
||||
v.aload(elementSerializer.asmType) // -> length, arr, index, obj
|
||||
v.castIfNeeded(elementSerializer.asmType)
|
||||
|
||||
v.load(1, PARCEL_TYPE) // -> length, arr, index, obj, parcel
|
||||
v.swap() // -> length, arr, index, parcel, obj
|
||||
elementSerializer.writeValue(v) // -> length, arr, index
|
||||
|
||||
v.aconst(1) // -> length, arr, index, (1)
|
||||
v.add(Type.INT_TYPE) // -> length, arr, (index + 1)
|
||||
v.swap() // -> length, (index + 1), arr
|
||||
v.dupX2() // -> arr, length, (index + 1), arr
|
||||
v.pop() // -> arr, length, (index + 1)
|
||||
v.goTo(nextLoopIteration)
|
||||
|
||||
v.visitLabel(loopIsOver)
|
||||
v.pop2() // -> arr
|
||||
v.pop()
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> length
|
||||
v.dup() // -> length, length
|
||||
v.newarray(elementSerializer.asmType) // -> length, arr
|
||||
v.swap() // -> arr, length
|
||||
v.aconst(0) // -> arr, length, index
|
||||
|
||||
val nextLoopIteration = Label()
|
||||
val loopIsOver = Label()
|
||||
|
||||
v.visitLabel(nextLoopIteration)
|
||||
v.dup2() // -> arr, length, index, length, index
|
||||
v.ificmple(loopIsOver) // -> arr, length, index
|
||||
|
||||
v.swap() // -> arr, index, length
|
||||
v.dupX2() // -> length, arr, index, length
|
||||
v.pop() // -> length, arr, index
|
||||
v.dup2() // -> length, arr, index, arr, index
|
||||
|
||||
v.load(1, PARCEL_TYPE) // -> length, arr, index, arr, index, parcel
|
||||
elementSerializer.readValue(v) // -> length, arr, index, arr, index, obj
|
||||
v.castIfNeeded(elementSerializer.asmType)
|
||||
v.astore(elementSerializer.asmType) // -> length, arr, index
|
||||
v.aconst(1) // -> length, arr, index, (1)
|
||||
v.add(Type.INT_TYPE) // -> length, arr, (index + 1)
|
||||
v.swap() // -> length, (index + 1), arr
|
||||
v.dupX2() // -> arr, length, (index + 1), arr
|
||||
v.pop() // -> arr, length, (index + 1)
|
||||
v.goTo(nextLoopIteration)
|
||||
|
||||
v.visitLabel(loopIsOver)
|
||||
v.pop2() // -> arr
|
||||
}
|
||||
}
|
||||
|
||||
internal fun InstructionAdapter.castIfNeeded(targetType: Type) {
|
||||
if (targetType.sort != Type.OBJECT && targetType.sort != Type.ARRAY) return
|
||||
if (targetType.descriptor == "Ljava/lang/Object;") return
|
||||
checkcast(targetType)
|
||||
}
|
||||
|
||||
internal class ListSetParcelSerializer(
|
||||
asmType: Type,
|
||||
elementSerializer: ParcelSerializer
|
||||
) : AbstractCollectionParcelSerializer(asmType, elementSerializer) {
|
||||
override fun getSize(v: InstructionAdapter) {
|
||||
v.invokeinterface("java/util/Collection", "size", "()I")
|
||||
}
|
||||
|
||||
override fun getIterator(v: InstructionAdapter) {
|
||||
v.invokeinterface("java/util/Collection", "iterator", "()Ljava/util/Iterator;")
|
||||
}
|
||||
|
||||
override fun doWriteValue(v: InstructionAdapter) {
|
||||
// -> parcel, obj
|
||||
v.castIfNeeded(elementSerializer.asmType)
|
||||
elementSerializer.writeValue(v)
|
||||
}
|
||||
|
||||
override fun doReadValue(v: InstructionAdapter) {
|
||||
// -> collection, parcel
|
||||
|
||||
elementSerializer.readValue(v) // -> collection, element
|
||||
v.castIfNeeded(elementSerializer.asmType)
|
||||
|
||||
v.invokevirtual(collectionType.internalName, "add", "(Ljava/lang/Object;)Z", false) // -> bool
|
||||
v.pop()
|
||||
}
|
||||
}
|
||||
|
||||
internal class MapParcelSerializer(
|
||||
asmType: Type,
|
||||
private val keySerializer: ParcelSerializer,
|
||||
elementSerializer: ParcelSerializer
|
||||
) : AbstractCollectionParcelSerializer(asmType, elementSerializer) {
|
||||
override fun getSize(v: InstructionAdapter) {
|
||||
v.invokeinterface("java/util/Map", "size", "()I")
|
||||
}
|
||||
|
||||
override fun getIterator(v: InstructionAdapter) {
|
||||
v.invokeinterface("java/util/Map", "entrySet", "()Ljava/util/Set;")
|
||||
v.invokeinterface("java/util/Set", "iterator", "()Ljava/util/Iterator;")
|
||||
}
|
||||
|
||||
override fun doWriteValue(v: InstructionAdapter) {
|
||||
// -> parcel, obj
|
||||
|
||||
v.dup2() // -> parcel, obj, parcel, obj
|
||||
|
||||
v.invokeinterface("java/util/Map\$Entry", "getKey", "()Ljava/lang/Object;") // -> parcel, obj, parcel, key
|
||||
v.castIfNeeded(keySerializer.asmType)
|
||||
keySerializer.writeValue(v) // -> parcel, obj
|
||||
|
||||
v.invokeinterface("java/util/Map\$Entry", "getValue", "()Ljava/lang/Object;") // -> parcel, value
|
||||
v.castIfNeeded(elementSerializer.asmType)
|
||||
elementSerializer.writeValue(v)
|
||||
}
|
||||
|
||||
override fun doReadValue(v: InstructionAdapter) {
|
||||
// -> map, parcel
|
||||
v.dup() // -> map, parcel, parcel
|
||||
|
||||
keySerializer.readValue(v) // -> map, parcel, key
|
||||
v.castIfNeeded(keySerializer.asmType)
|
||||
|
||||
v.swap() // -> map, key, parcel
|
||||
|
||||
elementSerializer.readValue(v) // -> map, key, value
|
||||
v.castIfNeeded(elementSerializer.asmType)
|
||||
|
||||
v.invokeinterface("java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") // -> obj
|
||||
v.pop()
|
||||
}
|
||||
}
|
||||
|
||||
abstract internal class AbstractCollectionParcelSerializer(
|
||||
final override val asmType: Type,
|
||||
protected val elementSerializer: ParcelSerializer
|
||||
) : ParcelSerializer {
|
||||
protected val collectionType: Type = Type.getObjectType(when (asmType.internalName) {
|
||||
"java/util/List" -> "java/util/ArrayList"
|
||||
"java/util/Set" -> "java/util/LinkedHashSet"
|
||||
"java/util/Map" -> "java/util/LinkedHashMap"
|
||||
else -> asmType.internalName
|
||||
})
|
||||
|
||||
private var hasConstructorWithCapacity = when (collectionType.internalName) {
|
||||
"java/util/LinkedList", "java/util/TreeSet", "java/util/TreeMap" -> false
|
||||
else -> true
|
||||
}
|
||||
|
||||
/**
|
||||
* Stack before: collection
|
||||
* Stack after: size
|
||||
*/
|
||||
protected abstract fun getSize(v: InstructionAdapter)
|
||||
|
||||
/**
|
||||
* Stack before: collection
|
||||
* Stack after: iterator
|
||||
*/
|
||||
protected abstract fun getIterator(v: InstructionAdapter)
|
||||
|
||||
/**
|
||||
* Stack before: parcel, obj
|
||||
* Stack after: <empty>
|
||||
*/
|
||||
protected abstract fun doWriteValue(v: InstructionAdapter)
|
||||
|
||||
/**
|
||||
* Stack before: collection, parcel
|
||||
* Stack after: <empty>
|
||||
*/
|
||||
protected abstract fun doReadValue(v: InstructionAdapter)
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
val labelIteratorLoop = Label()
|
||||
val labelReturn = Label()
|
||||
|
||||
v.dupX1() // -> collection, parcel, collection
|
||||
getSize(v) // -> collection, parcel, size
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false) // collection
|
||||
getIterator(v) // -> iterator
|
||||
|
||||
v.visitLabel(labelIteratorLoop)
|
||||
v.dup() // -> iterator, iterator
|
||||
v.invokeinterface("java/util/Iterator", "hasNext", "()Z") // -> iterator, hasNext
|
||||
v.ifeq(labelReturn) // -> iterator
|
||||
|
||||
v.dup() // -> iterator, iterator
|
||||
v.invokeinterface("java/util/Iterator", "next", "()Ljava/lang/Object;") // -> iterator, obj
|
||||
|
||||
v.load(1, PARCEL_TYPE) // -> iterator, obj, parcel
|
||||
v.swap() // -> iterator, parcel, obj
|
||||
doWriteValue(v) // -> iterator
|
||||
|
||||
v.goTo(labelIteratorLoop)
|
||||
|
||||
v.visitLabel(labelReturn)
|
||||
v.pop()
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
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
|
||||
v.load(1, PARCEL_TYPE) // -> size, list, list, parcel
|
||||
doReadValue(v) // -> size, list
|
||||
|
||||
v.swap() // -> list, size
|
||||
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.pop()
|
||||
}
|
||||
}
|
||||
|
||||
internal class SparseArrayParcelSerializer(override val asmType: Type, private val valueSerializer: ParcelSerializer) : ParcelSerializer {
|
||||
private val valueType = (valueSerializer as? PrimitiveTypeParcelSerializer)?.asmType ?: Type.getObjectType("java/lang/Object")
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.dup() // -> parcel, arr, arr
|
||||
v.invokevirtual(asmType.internalName, "size", "()I", false) // -> parcel, arr, size
|
||||
v.dup2X1() // -> arr, size, parcel, arr, size
|
||||
v.swap() // -> arr, size, parcel, size, arr
|
||||
v.pop() // -> arr, size, parcel, size
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false) // -> arr, size
|
||||
|
||||
v.aconst(0) // -> arr, size, <index>
|
||||
|
||||
val nextLoopIteration = Label()
|
||||
val loopIsOver = Label()
|
||||
|
||||
v.visitLabel(nextLoopIteration)
|
||||
v.dup2() // -> arr, size, index, size, index
|
||||
v.ificmple(loopIsOver) // -> arr, size, index
|
||||
|
||||
v.swap() // -> arr, index, size
|
||||
v.dupX2() // -> size, arr, index, size
|
||||
v.pop() // -> size, arr, index
|
||||
v.dup2() // -> size, arr, index, arr, index
|
||||
v.invokevirtual(asmType.internalName, "keyAt", "(I)I", false) // -> size, arr, index, key
|
||||
v.load(1, PARCEL_TYPE) // size, arr, index, key, parcel
|
||||
v.dupX2() // -> size, arr, parcel, index, key, parcel
|
||||
v.swap() // -> size, arr, parcel, index, parcel, key
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false) // -> size, arr, parcel, index
|
||||
|
||||
v.swap() // -> size, arr, index, parcel
|
||||
v.dupX2() // -> size, parcel, arr, index, parcel
|
||||
v.pop() // -> size, parcel, arr, index
|
||||
v.dup2X1() // -> size, arr, index, parcel, arr, index
|
||||
v.invokevirtual(asmType.internalName, "valueAt", "(I)${valueType.descriptor}", false) // -> size, arr, index, parcel, value
|
||||
valueSerializer.writeValue(v) // -> size, arr, index
|
||||
|
||||
v.aconst(1) // -> size, arr, index, (1)
|
||||
v.add(Type.INT_TYPE) // -> size, arr, (index + 1)
|
||||
v.dup2X1() // -> arr, (index + 1), size, arr, (index + 1)
|
||||
v.pop2() // -> arr, (index + 1), size
|
||||
v.swap() // -> arr, size, (index + 1)
|
||||
v.goTo(nextLoopIteration)
|
||||
|
||||
v.visitLabel(loopIsOver)
|
||||
v.pop2()
|
||||
v.pop()
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
val nextLoopIteration = Label()
|
||||
val loopIsOver = Label()
|
||||
|
||||
v.visitLabel(nextLoopIteration)
|
||||
v.dup() // -> arr, size, size
|
||||
v.ifle(loopIsOver) // -> arr, size
|
||||
v.swap() // -> size, arr
|
||||
v.dupX1() // -> arr, size, arr
|
||||
|
||||
v.load(1, PARCEL_TYPE) // -> arr, size, arr, parcel
|
||||
v.dup() // -> arr, size, arr, parcel, parcel
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> arr, size, arr, parcel, key
|
||||
v.swap() // -> arr, size, arr, key, parcel
|
||||
valueSerializer.readValue(v) // -> arr, size, arr, key, value
|
||||
v.invokevirtual(asmType.internalName, "put", "(I${valueType.descriptor})V", false) // -> arr, size
|
||||
v.aconst(-1) // -> arr, size, (-1)
|
||||
v.add(Type.INT_TYPE) // -> arr, (size - 1)
|
||||
v.goTo(nextLoopIteration)
|
||||
|
||||
v.visitLabel(loopIsOver)
|
||||
v.pop() // -> arr
|
||||
}
|
||||
}
|
||||
|
||||
internal class NullAwareParcelSerializerWrapper(val delegate: ParcelSerializer) : ParcelSerializer {
|
||||
override val asmType: Type
|
||||
get() = delegate.asmType
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) = writeValueNullAware(v) {
|
||||
delegate.writeValue(v)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) = readValueNullAware(v) {
|
||||
delegate.readValue(v)
|
||||
}
|
||||
}
|
||||
|
||||
/** write...() and get...() methods in Android should support passing `null` values. */
|
||||
internal class NullCompliantObjectParcelSerializer(
|
||||
override val asmType: Type,
|
||||
writeMethod: Method<String?>,
|
||||
readMethod: Method<String?>
|
||||
) : ParcelSerializer {
|
||||
private val writeMethod = Method(writeMethod.name, writeMethod.signature ?: "(${asmType.descriptor})V")
|
||||
private val readMethod = Method(readMethod.name, readMethod.signature ?: "()${asmType.descriptor}")
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, writeMethod.name, writeMethod.signature, false)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, readMethod.name, readMethod.signature, false)
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
internal class BoxedPrimitiveTypeParcelSerializer private constructor(val unboxedType: Type) : ParcelSerializer {
|
||||
companion object {
|
||||
private val BOXED_TO_UNBOXED_TYPE_MAPPINGS = mapOf(
|
||||
"java/lang/Boolean" to Type.BOOLEAN_TYPE,
|
||||
"java/lang/Character" to Type.CHAR_TYPE,
|
||||
"java/lang/Byte" to Type.BYTE_TYPE,
|
||||
"java/lang/Short" to Type.SHORT_TYPE,
|
||||
"java/lang/Integer" to Type.INT_TYPE,
|
||||
"java/lang/Float" to Type.FLOAT_TYPE,
|
||||
"java/lang/Long" to Type.LONG_TYPE,
|
||||
"java/lang/Double" to Type.DOUBLE_TYPE)
|
||||
|
||||
private val UNBOXED_TO_BOXED_TYPE_MAPPINGS = BOXED_TO_UNBOXED_TYPE_MAPPINGS.map { it.value to it.key }.toMap()
|
||||
|
||||
private val BOXED_VALUE_METHOD_NAMES = mapOf(
|
||||
"java/lang/Boolean" to "booleanValue",
|
||||
"java/lang/Character" to "charValue",
|
||||
"java/lang/Byte" to "byteValue",
|
||||
"java/lang/Short" to "shortValue",
|
||||
"java/lang/Integer" to "intValue",
|
||||
"java/lang/Float" to "floatValue",
|
||||
"java/lang/Long" to "longValue",
|
||||
"java/lang/Double" to "doubleValue")
|
||||
|
||||
private val INSTANCES = BOXED_TO_UNBOXED_TYPE_MAPPINGS.values.map { type ->
|
||||
type to BoxedPrimitiveTypeParcelSerializer(type)
|
||||
}.toMap()
|
||||
|
||||
fun forUnboxedType(type: Type) = INSTANCES[type] ?: error("Unsupported type $type")
|
||||
fun forBoxedType(type: Type) = INSTANCES[BOXED_TO_UNBOXED_TYPE_MAPPINGS[type.internalName]] ?: error("Unsupported type $type")
|
||||
}
|
||||
|
||||
override val asmType: Type = Type.getObjectType(UNBOXED_TO_BOXED_TYPE_MAPPINGS[unboxedType] ?: error("Unsupported type $unboxedType"))
|
||||
val unboxedSerializer = PrimitiveTypeParcelSerializer.getInstance(unboxedType)
|
||||
val typeValueMethodName = BOXED_VALUE_METHOD_NAMES[asmType.internalName]!!
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(asmType.internalName, typeValueMethodName, "()${unboxedType.descriptor}", false)
|
||||
unboxedSerializer.writeValue(v)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
unboxedSerializer.readValue(v)
|
||||
v.invokestatic(asmType.internalName, "valueOf", "(${unboxedType.descriptor})${asmType.descriptor}", false)
|
||||
}
|
||||
}
|
||||
|
||||
internal open class PrimitiveTypeParcelSerializer private constructor(final override val asmType: Type) : ParcelSerializer {
|
||||
companion object {
|
||||
private val WRITE_METHOD_NAMES = mapOf(
|
||||
Type.BOOLEAN_TYPE to Method("writeInt", "(I)V"),
|
||||
Type.CHAR_TYPE to Method("writeInt", "(I)V"),
|
||||
Type.BYTE_TYPE to Method("writeByte", "(B)V"),
|
||||
Type.SHORT_TYPE to Method("writeInt", "(I)V"),
|
||||
Type.INT_TYPE to Method("writeInt", "(I)V"),
|
||||
Type.FLOAT_TYPE to Method("writeFloat", "(F)V"),
|
||||
Type.LONG_TYPE to Method("writeLong", "(J)V"),
|
||||
Type.DOUBLE_TYPE to Method("writeDouble", "(D)V"))
|
||||
|
||||
private val READ_METHOD_NAMES = mapOf(
|
||||
Type.BOOLEAN_TYPE to Method("readInt", "()I"),
|
||||
Type.CHAR_TYPE to Method("readInt", "()I"),
|
||||
Type.BYTE_TYPE to Method("readByte", "()B"),
|
||||
Type.SHORT_TYPE to Method("readInt", "()I"),
|
||||
Type.INT_TYPE to Method("readInt", "()I"),
|
||||
Type.FLOAT_TYPE to Method("readFloat", "()F"),
|
||||
Type.LONG_TYPE to Method("readLong", "()J"),
|
||||
Type.DOUBLE_TYPE to Method("readDouble", "()D"))
|
||||
|
||||
private val INSTANCES = READ_METHOD_NAMES.keys.map {
|
||||
it to when (it) {
|
||||
Type.CHAR_TYPE -> CharParcelSerializer
|
||||
Type.SHORT_TYPE -> ShortParcelSerializer
|
||||
else -> PrimitiveTypeParcelSerializer(it)
|
||||
}
|
||||
}.toMap()
|
||||
|
||||
fun getInstance(type: Type) = INSTANCES[type] ?: error("Unsupported type ${type.descriptor}")
|
||||
}
|
||||
|
||||
object CharParcelSerializer : PrimitiveTypeParcelSerializer(Type.CHAR_TYPE) {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.cast(Type.CHAR_TYPE, Type.INT_TYPE)
|
||||
super.writeValue(v)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
super.readValue(v)
|
||||
v.cast(Type.INT_TYPE, Type.CHAR_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
object ShortParcelSerializer : PrimitiveTypeParcelSerializer(Type.SHORT_TYPE) {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.cast(Type.SHORT_TYPE, Type.INT_TYPE)
|
||||
super.writeValue(v)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
super.readValue(v)
|
||||
v.cast(Type.INT_TYPE, Type.SHORT_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
private val writeMethod = WRITE_METHOD_NAMES[asmType]!!
|
||||
private val readMethod = READ_METHOD_NAMES[asmType]!!
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, writeMethod.name, writeMethod.signature, false)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, readMethod.name, readMethod.signature, false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readValueNullAware(v: InstructionAdapter, block: () -> Unit) {
|
||||
val labelNull = Label()
|
||||
val labelReturn = Label()
|
||||
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false)
|
||||
v.ifeq(labelNull)
|
||||
|
||||
v.load(1, PARCEL_TYPE)
|
||||
block()
|
||||
v.goTo(labelReturn)
|
||||
|
||||
// Just push null on stack if the value is null
|
||||
v.visitLabel(labelNull)
|
||||
v.aconst(null)
|
||||
|
||||
v.visitLabel(labelReturn)
|
||||
}
|
||||
|
||||
private fun writeValueNullAware(v: InstructionAdapter, block: () -> Unit) {
|
||||
val labelReturn = Label()
|
||||
val labelNull = Label()
|
||||
v.dup()
|
||||
v.ifnull(labelNull)
|
||||
|
||||
// Write 1 if non-null, 0 if null
|
||||
|
||||
v.load(1, PARCEL_TYPE)
|
||||
v.aconst(1)
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false)
|
||||
block()
|
||||
|
||||
v.goTo(labelReturn)
|
||||
|
||||
labelNull@ v.visitLabel(labelNull)
|
||||
v.pop()
|
||||
v.aconst(0)
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false)
|
||||
|
||||
labelReturn@ v.visitLabel(labelReturn)
|
||||
}
|
||||
|
||||
internal class Method<out T : String?>(val name: String, val signature: T) {
|
||||
companion object {
|
||||
operator fun invoke(name: String) = Method(name, null)
|
||||
}
|
||||
}
|
||||
+15
@@ -18,6 +18,9 @@ package org.jetbrains.kotlin.android.synthetic
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableCodegenExtension
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableResolveExtension
|
||||
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidExtensionsExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.android.synthetic.diagnostic.AndroidExtensionPropertiesCallChecker
|
||||
@@ -41,6 +44,8 @@ import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
import org.jetbrains.kotlin.android.synthetic.codegen.ParcelableClinitClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
|
||||
object AndroidConfigurationKeys {
|
||||
val VARIANT: CompilerConfigurationKey<List<String>> = CompilerConfigurationKey.create<List<String>>("Android build variant")
|
||||
@@ -75,7 +80,17 @@ class AndroidCommandLineProcessor : CommandLineProcessor {
|
||||
}
|
||||
|
||||
class AndroidComponentRegistrar : ComponentRegistrar {
|
||||
companion object {
|
||||
fun registerParcelExtensions(project: Project) {
|
||||
ExpressionCodegenExtension.registerExtension(project, ParcelableCodegenExtension())
|
||||
SyntheticResolveExtension.registerExtension(project, ParcelableResolveExtension())
|
||||
ClassBuilderInterceptorExtension.registerExtension(project, ParcelableClinitClassBuilderInterceptorExtension())
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
||||
registerParcelExtensions(project)
|
||||
|
||||
val applicationPackage = configuration.get(AndroidConfigurationKeys.PACKAGE)
|
||||
val variants = configuration.get(AndroidConfigurationKeys.VARIANT)?.mapNotNull { parseVariant(it) } ?: emptyList()
|
||||
val isExperimental = configuration.get(AndroidConfigurationKeys.EXPERIMENTAL) == "true"
|
||||
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
package android.os
|
||||
|
||||
class Parcel
|
||||
|
||||
interface Parcelable {
|
||||
fun describeContents(): Int
|
||||
|
||||
fun writeToParcel(parcel: Parcel, flags: Int)
|
||||
|
||||
interface Creator<T> {
|
||||
fun createFromParcel(parcel: Parcel): T
|
||||
fun newArray(size: Int): Array<T>
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
@MagicParcel
|
||||
data class Test(val a: Array<String>) : Parcelable {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other?.javaClass != javaClass) return false
|
||||
|
||||
other as Test
|
||||
|
||||
if (!Arrays.equals(a, other.a)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode() = Arrays.hashCode(a)
|
||||
}
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Test(arrayOf("A", "B"))
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
@MagicParcel
|
||||
data class Test(
|
||||
val a: Array<String>,
|
||||
val b: Array<String?>,
|
||||
val c: IntArray,
|
||||
val d: CharArray?,
|
||||
val e: Array<IntArray>,
|
||||
val f: Array<List<String>>,
|
||||
val g: List<Array<String>>,
|
||||
val h: Array<String>?
|
||||
) : Parcelable {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other?.javaClass != javaClass) return false
|
||||
|
||||
other as Test
|
||||
|
||||
if (!Arrays.equals(a, other.a)) return false
|
||||
if (!Arrays.equals(b, other.b)) return false
|
||||
if (!Arrays.equals(c, other.c)) return false
|
||||
if (!Arrays.equals(d, other.d)) return false
|
||||
if (!Arrays.deepEquals(e, other.e)) return false
|
||||
if (!Arrays.equals(f, other.f)) return false
|
||||
|
||||
if (g.size != other.g.size) return false
|
||||
if (!g.zip(other.g).all { (f, s) -> Arrays.equals(f, s) }) return false
|
||||
|
||||
if (!Arrays.equals(h, other.h)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = Arrays.hashCode(a)
|
||||
result = 31 * result + Arrays.hashCode(b)
|
||||
result = 31 * result + Arrays.hashCode(c)
|
||||
result = 31 * result + (d?.let { Arrays.hashCode(it) } ?: 0)
|
||||
result = 31 * result + Arrays.hashCode(e)
|
||||
result = 31 * result + Arrays.hashCode(f)
|
||||
result = 31 * result + g.hashCode()
|
||||
result = 31 * result + (h?.let { Arrays.hashCode(it) } ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Test(
|
||||
a = arrayOf("A", "B", "C"),
|
||||
b = arrayOf("A", null, "B"),
|
||||
c = intArrayOf(1, 2, 3),
|
||||
d = null,
|
||||
e = arrayOf(intArrayOf(2, 4, 1), intArrayOf(10, 20)),
|
||||
f = arrayOf(listOf("A"), listOf("B", "C")),
|
||||
g = listOf(arrayOf("Z", "X"), arrayOf()),
|
||||
h = arrayOf("")
|
||||
)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@MagicParcel
|
||||
data class BoxedTypes(
|
||||
val boo: java.lang.Boolean,
|
||||
val c: java.lang.Character,
|
||||
val byt: java.lang.Byte,
|
||||
val s: java.lang.Short,
|
||||
val i: java.lang.Integer,
|
||||
val f: java.lang.Float,
|
||||
val l: java.lang.Long,
|
||||
val d: java.lang.Double
|
||||
) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = BoxedTypes(
|
||||
true as java.lang.Boolean,
|
||||
'#' as java.lang.Character,
|
||||
3.toByte() as java.lang.Byte,
|
||||
10.toShort() as java.lang.Short,
|
||||
-300 as java.lang.Integer,
|
||||
-5.0f as java.lang.Float,
|
||||
Long.MAX_VALUE as java.lang.Long,
|
||||
3.14 as java.lang.Double)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val first2 = readFromParcel<BoxedTypes>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.os.Bundle
|
||||
|
||||
@MagicParcel
|
||||
data class User(val a: Bundle) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = User(Bundle().apply { putChar("A", 'c'); putByte("B", 40.toByte()); putString("C", "ABC") })
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val test2 = readFromParcel<User>(parcel)
|
||||
|
||||
assert(compareBundles(test.a, test2.a))
|
||||
}
|
||||
|
||||
private fun compareBundles(first: Bundle, second: Bundle): Boolean {
|
||||
if (first.size() != second.size()) return false
|
||||
|
||||
for (key in first.keySet()) {
|
||||
if (first.get(key) != second.get(key)) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// WITH_RUNTIME
|
||||
// FULL_JDK
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.*
|
||||
|
||||
@MagicParcel
|
||||
data class Test(
|
||||
val a: List<String>,
|
||||
val b: MutableList<String>,
|
||||
val c: ArrayList<String>,
|
||||
val d: LinkedList<String>,
|
||||
val e: Set<String>,
|
||||
val f: MutableSet<String>,
|
||||
val g: TreeSet<String>,
|
||||
val h: HashSet<String>,
|
||||
val i: LinkedHashSet<String>
|
||||
) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Test(
|
||||
a = listOf("A"),
|
||||
b = mutableListOf("B"),
|
||||
c = ArrayList<String>().apply { this += "C" },
|
||||
d = LinkedList<String>().apply { this += "D" },
|
||||
e = setOf("E"),
|
||||
f = mutableSetOf("F"),
|
||||
g = TreeSet<String>().apply { this += "G" },
|
||||
h = HashSet<String>().apply { this += "H" },
|
||||
i = LinkedHashSet<String>().apply { this += "I" }
|
||||
)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
assert((first.d as LinkedList<*>).size == 1)
|
||||
assert((first2.h as HashSet<*>).size == 1)
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@MagicParcel
|
||||
data class Test(val a: List<String>) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Test(listOf("A", "B"))
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@MagicParcel
|
||||
data class Test(
|
||||
val a: List<String>,
|
||||
val b: List<String?>,
|
||||
val c: List<Int>,
|
||||
val d: List<Int?>,
|
||||
val e: List<List<String>?>,
|
||||
val f: List<List<List<Int>>>
|
||||
) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Test(
|
||||
a = listOf("A", "B"),
|
||||
b = listOf("A", null, "C"),
|
||||
c = listOf(1, 2, 3),
|
||||
d = listOf(1, null, 5),
|
||||
e = listOf(listOf("A", "B"), listOf(), null),
|
||||
f = listOf(listOf(listOf(1, 2), listOf(3)), listOf(listOf(5, 3)))
|
||||
)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
|
||||
assert(first2.a == listOf("A", "B"))
|
||||
assert(first2.b.size == 3)
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// WITH_RUNTIME
|
||||
// FULL_JDK
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.*
|
||||
|
||||
@MagicParcel
|
||||
data class Test(
|
||||
val a: Map<String, String>,
|
||||
val b: MutableMap<String, String>,
|
||||
val c: HashMap<String, String>,
|
||||
val d: LinkedHashMap<String, String>,
|
||||
val e: TreeMap<String, String>
|
||||
) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Test(
|
||||
a = mapOf("A" to "B"),
|
||||
b = mutableMapOf("A" to "B"),
|
||||
c = HashMap<String, String>().apply { put("A", "B") },
|
||||
d = LinkedHashMap<String, String>().apply { put("A", "B") },
|
||||
e = TreeMap<String, String>().apply { put("A", "B") }
|
||||
)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
assert((first.c as HashMap<*, *>).size == 1)
|
||||
assert((first2.e as TreeMap<*, *>).size == 1)
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@MagicParcel
|
||||
data class Test(val a: Map<String, String>) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Test(mapOf("A" to "B", "C" to "D"))
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@MagicParcel
|
||||
data class Test(
|
||||
val a: Map<String, String>,
|
||||
val b: Map<String?, String>,
|
||||
val c: Map<String, String?>,
|
||||
val d: Map<String, Map<Int, String>>,
|
||||
val e: Map<Int?, List<String>>,
|
||||
val f: Map<Boolean, Boolean>,
|
||||
val g: Map<String, Map<String, Map<String, String>>>
|
||||
) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Test(
|
||||
a = mapOf("A" to "B", "C" to "D"),
|
||||
b = mapOf("A" to "B", null to "D", "E" to "F"),
|
||||
c = mapOf("A" to null, "C" to "D"),
|
||||
d = mapOf("A" to mapOf(1 to "", 2 to "x")),
|
||||
e = mapOf(1 to listOf("", ""), null to listOf()),
|
||||
f = mapOf(true to false, false to true),
|
||||
g = mapOf("A" to mapOf("B" to mapOf("C" to "D", "E" to "F")))
|
||||
)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
}
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@MagicParcel
|
||||
data class Test(
|
||||
val str1: String,
|
||||
val str2: String?,
|
||||
val int1: Int,
|
||||
val int2: Int?
|
||||
) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Test("John", "Smith", 20, 30)
|
||||
val second = Test("A", null, 20, null)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
second.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
val second2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
assert(second == second2)
|
||||
}
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@MagicParcel
|
||||
data class Test(val a: String?) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Test("John")
|
||||
val second = Test(null)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
second.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
val second2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
assert(second == second2)
|
||||
}
|
||||
Vendored
+38
@@ -0,0 +1,38 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@MagicParcel
|
||||
data class PrimitiveTypes(
|
||||
val boo: Boolean,
|
||||
val c: Char,
|
||||
val byt: Byte,
|
||||
val s: Short,
|
||||
val i: Int,
|
||||
val f: Float,
|
||||
val l: Long,
|
||||
val d: Double
|
||||
) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = PrimitiveTypes(true, '#', 3.toByte(), 10.toShort(), -300, -5.0f, Long.MAX_VALUE, 3.14)
|
||||
val second = PrimitiveTypes(false, '\n', Byte.MIN_VALUE, Short.MIN_VALUE, Int.MIN_VALUE, Float.POSITIVE_INFINITY,
|
||||
Long.MAX_VALUE, Double.NEGATIVE_INFINITY)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
second.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val first2 = readFromParcel<PrimitiveTypes>(parcel)
|
||||
val second2 = readFromParcel<PrimitiveTypes>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
assert(second == second2)
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@MagicParcel
|
||||
data class User(val firstName: String, val secondName: String, val age: Int) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val user = User("John", "Smith", 20)
|
||||
user.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val user2 = readFromParcel<User>(parcel)
|
||||
assert(user == user2)
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.util.*
|
||||
|
||||
@MagicParcel
|
||||
data class Data(val a: String, val b: String) : Parcelable
|
||||
|
||||
@MagicParcel
|
||||
data class User(val a: SparseIntArray, val b: SparseLongArray, val c: SparseArray<Data>) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val user = User(
|
||||
a = SparseIntArray().apply { put(1, 5); put(100, -1); put(1000, 0) },
|
||||
b = SparseLongArray().apply { put(3, 2); put(2, 3); put(10, 10) },
|
||||
c = SparseArray<Data>().apply { put(1, Data("A", "B")); put(10, Data("C", "D")); put(105, Data("E", "")) }
|
||||
)
|
||||
|
||||
user.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val user2 = readFromParcel<User>(parcel)
|
||||
|
||||
assert(compareSparseIntArrays(user.a, user2.a))
|
||||
assert(compareSparseLongArrays(user.b, user2.b))
|
||||
assert(compareSparseArrays(user.c, user2.c))
|
||||
}
|
||||
|
||||
private fun compareSparseIntArrays(first: SparseIntArray, second: SparseIntArray): Boolean {
|
||||
if (first === second) return true
|
||||
if (first.size() != second.size()) return false
|
||||
|
||||
for (i in 0 until first.size()) {
|
||||
if (first.keyAt(i) != second.keyAt(i)) return false
|
||||
if (first.valueAt(i) != second.valueAt(i)) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun compareSparseLongArrays(first: SparseLongArray, second: SparseLongArray): Boolean {
|
||||
if (first === second) return true
|
||||
if (first.size() != second.size()) return false
|
||||
|
||||
for (i in 0 until first.size()) {
|
||||
if (first.keyAt(i) != second.keyAt(i)) return false
|
||||
if (first.valueAt(i) != second.valueAt(i)) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun compareSparseArrays(first: SparseArray<*>, second: SparseArray<*>): Boolean {
|
||||
if (first === second) return true
|
||||
if (first.size() != second.size()) return false
|
||||
|
||||
for (i in 0 until first.size()) {
|
||||
if (first.keyAt(i) != second.keyAt(i)) return false
|
||||
if (first.valueAt(i) != second.valueAt(i)) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.util.SparseBooleanArray
|
||||
|
||||
@MagicParcel
|
||||
data class User(val a: SparseBooleanArray) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = User(SparseBooleanArray().apply { put(1, false); put(5, true); put(1000, false) })
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
val test2 = readFromParcel<User>(parcel)
|
||||
|
||||
assert(compareSparseBooleanArrays(test.a, test2.a))
|
||||
}
|
||||
|
||||
private fun compareSparseBooleanArrays(first: SparseBooleanArray, second: SparseBooleanArray): Boolean {
|
||||
if (first === second) return true
|
||||
if (first.size() != second.size()) return false
|
||||
|
||||
for (i in 0 until first.size()) {
|
||||
if (first.keyAt(i) != second.keyAt(i)) return false
|
||||
if (first.valueAt(i) != second.valueAt(i)) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package test
|
||||
|
||||
import android.os.Parcel
|
||||
|
||||
fun parcelTest(block: (Parcel) -> Unit): String {
|
||||
val parcel = Parcel.obtain()
|
||||
try {
|
||||
block(parcel)
|
||||
return "OK"
|
||||
} finally {
|
||||
parcel.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T> readFromParcel(parcel: Parcel): T {
|
||||
val creator = T::class.java.getDeclaredField("CREATOR").get(null)
|
||||
return creator::class.java.getDeclaredMethod("createFromParcel", Parcel::class.java).invoke(creator, parcel) as T
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
// CURIOUS_ABOUT writeToParcel
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
|
||||
@MagicParcel
|
||||
class Test(val names: List<List<List<String>>>)
|
||||
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
public static class Test$CREATOR : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final Test createFromParcel(android.os.Parcel p0)
|
||||
|
||||
public final Test[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class Test : java/lang/Object {
|
||||
public final static Test$CREATOR CREATOR
|
||||
|
||||
private final java.util.List names
|
||||
|
||||
static void <clinit>()
|
||||
|
||||
public void <init>(java.util.List p0)
|
||||
|
||||
public final int describeContents()
|
||||
|
||||
public final java.util.List getNames()
|
||||
|
||||
public final void writeToParcel(android.os.Parcel p0, int p1) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (names, Ljava/util/List;)
|
||||
DUP_X1
|
||||
INVOKEINTERFACE (java/util/Collection, size, ()I)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
INVOKEINTERFACE (java/util/Collection, iterator, ()Ljava/util/Iterator;)
|
||||
LABEL (L1)
|
||||
DUP
|
||||
INVOKEINTERFACE (java/util/Iterator, hasNext, ()Z)
|
||||
IFEQ (L2)
|
||||
DUP
|
||||
INVOKEINTERFACE (java/util/Iterator, next, ()Ljava/lang/Object;)
|
||||
ALOAD (1)
|
||||
SWAP
|
||||
CHECKCAST
|
||||
DUP_X1
|
||||
INVOKEINTERFACE (java/util/Collection, size, ()I)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
INVOKEINTERFACE (java/util/Collection, iterator, ()Ljava/util/Iterator;)
|
||||
LABEL (L3)
|
||||
DUP
|
||||
INVOKEINTERFACE (java/util/Iterator, hasNext, ()Z)
|
||||
IFEQ (L4)
|
||||
DUP
|
||||
INVOKEINTERFACE (java/util/Iterator, next, ()Ljava/lang/Object;)
|
||||
ALOAD (1)
|
||||
SWAP
|
||||
CHECKCAST
|
||||
DUP_X1
|
||||
INVOKEINTERFACE (java/util/Collection, size, ()I)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
INVOKEINTERFACE (java/util/Collection, iterator, ()Ljava/util/Iterator;)
|
||||
LABEL (L5)
|
||||
DUP
|
||||
INVOKEINTERFACE (java/util/Iterator, hasNext, ()Z)
|
||||
IFEQ (L6)
|
||||
DUP
|
||||
INVOKEINTERFACE (java/util/Iterator, next, ()Ljava/lang/Object;)
|
||||
ALOAD (1)
|
||||
SWAP
|
||||
CHECKCAST
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeString, (Ljava/lang/String;)V)
|
||||
GOTO (L5)
|
||||
LABEL (L6)
|
||||
POP
|
||||
GOTO (L3)
|
||||
LABEL (L4)
|
||||
POP
|
||||
GOTO (L1)
|
||||
LABEL (L2)
|
||||
POP
|
||||
RETURN
|
||||
LABEL (L7)
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// CURIOUS_ABOUT writeToParcel
|
||||
|
||||
import android.util.Size
|
||||
import kotlinx.android.parcel.*
|
||||
|
||||
@MagicParcel
|
||||
class TestNullable(val a: Size?)
|
||||
|
||||
@MagicParcel
|
||||
class TestNotNull(val a: Size)
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
public static class TestNotNull$CREATOR : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final TestNotNull createFromParcel(android.os.Parcel p0)
|
||||
|
||||
public final TestNotNull[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class TestNotNull : java/lang/Object {
|
||||
public final static TestNotNull$CREATOR CREATOR
|
||||
|
||||
private final android.util.Size a
|
||||
|
||||
static void <clinit>()
|
||||
|
||||
public void <init>(android.util.Size p0)
|
||||
|
||||
public final int describeContents()
|
||||
|
||||
public final android.util.Size getA()
|
||||
|
||||
public final void writeToParcel(android.os.Parcel p0, int p1) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (a, Landroid/util/Size;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeSize, (Landroid/util/Size;)V)
|
||||
RETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestNullable$CREATOR : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final TestNullable createFromParcel(android.os.Parcel p0)
|
||||
|
||||
public final TestNullable[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class TestNullable : java/lang/Object {
|
||||
public final static TestNullable$CREATOR CREATOR
|
||||
|
||||
private final android.util.Size a
|
||||
|
||||
static void <clinit>()
|
||||
|
||||
public void <init>(android.util.Size p0)
|
||||
|
||||
public final int describeContents()
|
||||
|
||||
public final android.util.Size getA()
|
||||
|
||||
public final void writeToParcel(android.os.Parcel p0, int p1) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (a, Landroid/util/Size;)
|
||||
DUP
|
||||
IFNULL (L1)
|
||||
ALOAD (1)
|
||||
LDC (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeSize, (Landroid/util/Size;)V)
|
||||
GOTO (L2)
|
||||
LABEL (L1)
|
||||
POP
|
||||
LDC (0)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
LABEL (L2)
|
||||
RETURN
|
||||
LABEL (L3)
|
||||
}
|
||||
}
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
// CURIOUS_ABOUT writeToParcel, createFromParcel, <clinit>
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcelable
|
||||
import java.io.Serializable
|
||||
|
||||
class SerializableSimple(val a: String, val b: String) : Serializable
|
||||
|
||||
@MagicParcel
|
||||
class User(val notNull: SerializableSimple, val nullable: SerializableSimple) : Parcelable
|
||||
Vendored
+74
@@ -0,0 +1,74 @@
|
||||
public final class SerializableSimple : java/lang/Object, java/io/Serializable {
|
||||
private final java.lang.String a
|
||||
|
||||
private final java.lang.String b
|
||||
|
||||
public void <init>(java.lang.String p0, java.lang.String p1)
|
||||
|
||||
public final java.lang.String getA()
|
||||
|
||||
public final java.lang.String getB()
|
||||
}
|
||||
|
||||
public static class User$CREATOR : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final User createFromParcel(android.os.Parcel p0) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (in)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
NEW
|
||||
DUP
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readSerializable, ()LSerializableSimple;)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readSerializable, ()LSerializableSimple;)
|
||||
INVOKESPECIAL (User, <init>, (LSerializableSimple;LSerializableSimple;)V)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public final User[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class User : java/lang/Object, android/os/Parcelable {
|
||||
public final static User$CREATOR CREATOR
|
||||
|
||||
private final SerializableSimple notNull
|
||||
|
||||
private final SerializableSimple nullable
|
||||
|
||||
static void <clinit>() {
|
||||
NEW
|
||||
DUP
|
||||
INVOKESPECIAL (User$CREATOR, <init>, ()V)
|
||||
PUTSTATIC (CREATOR, LUser$CREATOR;)
|
||||
RETURN
|
||||
}
|
||||
|
||||
public void <init>(SerializableSimple p0, SerializableSimple p1)
|
||||
|
||||
public final int describeContents()
|
||||
|
||||
public final SerializableSimple getNotNull()
|
||||
|
||||
public final SerializableSimple getNullable()
|
||||
|
||||
public final void writeToParcel(android.os.Parcel p0, int p1) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (notNull, LSerializableSimple;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeSerializable, (LSerializableSimple;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (nullable, LSerializableSimple;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeSerializable, (LSerializableSimple;)V)
|
||||
RETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// CURIOUS_ABOUT writeToParcel, createFromParcel, <clinit>
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcelable
|
||||
|
||||
@MagicParcel
|
||||
class User(val firstName: String, val lastName: String, val age: Int, val isProUser: Boolean) : Parcelable
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
public static class User$CREATOR : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final User createFromParcel(android.os.Parcel p0) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (in)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
NEW
|
||||
DUP
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readString, ()Ljava/lang/String;)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readString, ()Ljava/lang/String;)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
|
||||
INVOKESPECIAL (User, <init>, (Ljava/lang/String;Ljava/lang/String;IZ)V)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public final User[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class User : java/lang/Object, android/os/Parcelable {
|
||||
public final static User$CREATOR CREATOR
|
||||
|
||||
private final int age
|
||||
|
||||
private final java.lang.String firstName
|
||||
|
||||
private final boolean isProUser
|
||||
|
||||
private final java.lang.String lastName
|
||||
|
||||
static void <clinit>() {
|
||||
NEW
|
||||
DUP
|
||||
INVOKESPECIAL (User$CREATOR, <init>, ()V)
|
||||
PUTSTATIC (CREATOR, LUser$CREATOR;)
|
||||
RETURN
|
||||
}
|
||||
|
||||
public void <init>(java.lang.String p0, java.lang.String p1, int p2, boolean p3)
|
||||
|
||||
public final int describeContents()
|
||||
|
||||
public final int getAge()
|
||||
|
||||
public final java.lang.String getFirstName()
|
||||
|
||||
public final java.lang.String getLastName()
|
||||
|
||||
public final boolean isProUser()
|
||||
|
||||
public final void writeToParcel(android.os.Parcel p0, int p1) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (firstName, Ljava/lang/String;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeString, (Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (lastName, Ljava/lang/String;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeString, (Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (age, I)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (isProUser, Z)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
RETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
// CURIOUS_ABOUT writeToParcel
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
|
||||
@MagicParcel
|
||||
class Test(val names: List<String>)
|
||||
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
public static class Test$CREATOR : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final Test createFromParcel(android.os.Parcel p0)
|
||||
|
||||
public final Test[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class Test : java/lang/Object {
|
||||
public final static Test$CREATOR CREATOR
|
||||
|
||||
private final java.util.List names
|
||||
|
||||
static void <clinit>()
|
||||
|
||||
public void <init>(java.util.List p0)
|
||||
|
||||
public final int describeContents()
|
||||
|
||||
public final java.util.List getNames()
|
||||
|
||||
public final void writeToParcel(android.os.Parcel p0, int p1) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (names, Ljava/util/List;)
|
||||
DUP_X1
|
||||
INVOKEINTERFACE (java/util/Collection, size, ()I)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
INVOKEINTERFACE (java/util/Collection, iterator, ()Ljava/util/Iterator;)
|
||||
LABEL (L1)
|
||||
DUP
|
||||
INVOKEINTERFACE (java/util/Iterator, hasNext, ()Z)
|
||||
IFEQ (L2)
|
||||
DUP
|
||||
INVOKEINTERFACE (java/util/Iterator, next, ()Ljava/lang/Object;)
|
||||
ALOAD (1)
|
||||
SWAP
|
||||
CHECKCAST
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeString, (Ljava/lang/String;)V)
|
||||
GOTO (L1)
|
||||
LABEL (L2)
|
||||
POP
|
||||
RETURN
|
||||
LABEL (L3)
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// CURIOUS_ABOUT writeToParcel, createFromParcel
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcelable
|
||||
import android.util.Size
|
||||
import android.util.SizeF
|
||||
|
||||
@MagicParcel
|
||||
data class Test(val size: Size, val nullable: Size?) : Parcelable
|
||||
|
||||
@MagicParcel
|
||||
data class TestF(val size: SizeF, val nullable: SizeF?) : Parcelable
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
public static class Test$CREATOR : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final Test createFromParcel(android.os.Parcel p0) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (in)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
LABEL (L1)
|
||||
NEW
|
||||
DUP
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readSize, ()Landroid/util/Size;)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
|
||||
IFEQ (L2)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readSize, ()Landroid/util/Size;)
|
||||
GOTO (L3)
|
||||
LABEL (L2)
|
||||
ACONST_NULL
|
||||
LABEL (L3)
|
||||
INVOKESPECIAL (Test, <init>, (Landroid/util/Size;Landroid/util/Size;)V)
|
||||
ARETURN
|
||||
LABEL (L4)
|
||||
}
|
||||
|
||||
public final Test[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class Test : java/lang/Object, android/os/Parcelable {
|
||||
public final static Test$CREATOR CREATOR
|
||||
|
||||
private final android.util.Size nullable
|
||||
|
||||
private final android.util.Size size
|
||||
|
||||
static void <clinit>()
|
||||
|
||||
public void <init>(android.util.Size p0, android.util.Size p1)
|
||||
|
||||
public final android.util.Size component1()
|
||||
|
||||
public final android.util.Size component2()
|
||||
|
||||
public final Test copy(android.util.Size p0, android.util.Size p1)
|
||||
|
||||
public static Test copy$default(Test p0, android.util.Size p1, android.util.Size p2, int p3, java.lang.Object p4)
|
||||
|
||||
public final int describeContents()
|
||||
|
||||
public boolean equals(java.lang.Object p0)
|
||||
|
||||
public final android.util.Size getNullable()
|
||||
|
||||
public final android.util.Size getSize()
|
||||
|
||||
public int hashCode()
|
||||
|
||||
public java.lang.String toString()
|
||||
|
||||
public final void writeToParcel(android.os.Parcel p0, int p1) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (size, Landroid/util/Size;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeSize, (Landroid/util/Size;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (nullable, Landroid/util/Size;)
|
||||
DUP
|
||||
IFNULL (L1)
|
||||
ALOAD (1)
|
||||
LDC (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeSize, (Landroid/util/Size;)V)
|
||||
GOTO (L2)
|
||||
LABEL (L1)
|
||||
POP
|
||||
LDC (0)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
LABEL (L2)
|
||||
RETURN
|
||||
LABEL (L3)
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestF$CREATOR : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final TestF createFromParcel(android.os.Parcel p0) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (in)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
LABEL (L1)
|
||||
NEW
|
||||
DUP
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readSizeF, ()Landroid/util/SizeF;)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
|
||||
IFEQ (L2)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readSizeF, ()Landroid/util/SizeF;)
|
||||
GOTO (L3)
|
||||
LABEL (L2)
|
||||
ACONST_NULL
|
||||
LABEL (L3)
|
||||
INVOKESPECIAL (TestF, <init>, (Landroid/util/SizeF;Landroid/util/SizeF;)V)
|
||||
ARETURN
|
||||
LABEL (L4)
|
||||
}
|
||||
|
||||
public final TestF[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class TestF : java/lang/Object, android/os/Parcelable {
|
||||
public final static TestF$CREATOR CREATOR
|
||||
|
||||
private final android.util.SizeF nullable
|
||||
|
||||
private final android.util.SizeF size
|
||||
|
||||
static void <clinit>()
|
||||
|
||||
public void <init>(android.util.SizeF p0, android.util.SizeF p1)
|
||||
|
||||
public final android.util.SizeF component1()
|
||||
|
||||
public final android.util.SizeF component2()
|
||||
|
||||
public final TestF copy(android.util.SizeF p0, android.util.SizeF p1)
|
||||
|
||||
public static TestF copy$default(TestF p0, android.util.SizeF p1, android.util.SizeF p2, int p3, java.lang.Object p4)
|
||||
|
||||
public final int describeContents()
|
||||
|
||||
public boolean equals(java.lang.Object p0)
|
||||
|
||||
public final android.util.SizeF getNullable()
|
||||
|
||||
public final android.util.SizeF getSize()
|
||||
|
||||
public int hashCode()
|
||||
|
||||
public java.lang.String toString()
|
||||
|
||||
public final void writeToParcel(android.os.Parcel p0, int p1) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (size, Landroid/util/SizeF;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeSizeF, (Landroid/util/SizeF;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (nullable, Landroid/util/SizeF;)
|
||||
DUP
|
||||
IFNULL (L1)
|
||||
ALOAD (1)
|
||||
LDC (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeSizeF, (Landroid/util/SizeF;)V)
|
||||
GOTO (L2)
|
||||
LABEL (L1)
|
||||
POP
|
||||
LDC (0)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
LABEL (L2)
|
||||
RETURN
|
||||
LABEL (L3)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user