Parcelize: Add the Parcelize compiler plugin with sources extracted from Android Extensions plugin (KT-40030)
This commit is contained in:
Generated
+3
@@ -10,7 +10,10 @@
|
||||
<w>kapt</w>
|
||||
<w>kotlinc</w>
|
||||
<w>mutators</w>
|
||||
<w>parcelable</w>
|
||||
<w>parceler</w>
|
||||
<w>parcelers</w>
|
||||
<w>parcelize</w>
|
||||
<w>repl</w>
|
||||
<w>testdata</w>
|
||||
<w>uast</w>
|
||||
|
||||
@@ -57,6 +57,7 @@ dependencies {
|
||||
testCompile(projectTests(":plugins:jvm-abi-gen"))
|
||||
testCompile(projectTests(":plugins:android-extensions-compiler"))
|
||||
testCompile(projectTests(":plugins:android-extensions-ide"))
|
||||
testCompile(projectTests(":plugins:parcelize:parcelize-compiler"))
|
||||
testCompile(projectTests(":kotlin-annotation-processing"))
|
||||
testCompile(projectTests(":kotlin-annotation-processing-cli"))
|
||||
testCompile(projectTests(":kotlin-allopen-compiler-plugin"))
|
||||
|
||||
@@ -174,6 +174,10 @@ import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenc
|
||||
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
|
||||
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
|
||||
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
||||
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBoxTest
|
||||
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBytecodeListingTest
|
||||
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBoxTest
|
||||
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBytecodeListingTest
|
||||
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
|
||||
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
|
||||
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverTest
|
||||
@@ -1508,6 +1512,24 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("plugins/parcelize/parcelize-compiler/tests", "plugins/parcelize/parcelize-compiler/testData") {
|
||||
testClass<AbstractParcelizeBoxTest> {
|
||||
model("box", targetBackend = TargetBackend.JVM)
|
||||
}
|
||||
|
||||
testClass<AbstractParcelizeIrBoxTest> {
|
||||
model("box", targetBackend = TargetBackend.JVM_IR)
|
||||
}
|
||||
|
||||
testClass<AbstractParcelizeBytecodeListingTest> {
|
||||
model("codegen", targetBackend = TargetBackend.JVM)
|
||||
}
|
||||
|
||||
testClass<AbstractParcelizeIrBytecodeListingTest> {
|
||||
model("codegen", targetBackend = TargetBackend.JVM_IR)
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("plugins/jvm-abi-gen/test", "plugins/jvm-abi-gen/testData") {
|
||||
testClass<AbstractCompareJvmAbiTest> {
|
||||
model("compare", recursive = false, extension = null)
|
||||
|
||||
@@ -148,6 +148,10 @@ import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenc
|
||||
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
|
||||
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
|
||||
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
||||
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBoxTest
|
||||
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBytecodeListingTest
|
||||
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBoxTest
|
||||
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBytecodeListingTest
|
||||
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
|
||||
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
|
||||
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverTest
|
||||
@@ -1127,6 +1131,24 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("plugins/parcelize/parcelize-compiler/tests", "plugins/parcelize/parcelize-compiler/testData") {
|
||||
testClass<AbstractParcelizeBoxTest> {
|
||||
model("box", targetBackend = TargetBackend.JVM)
|
||||
}
|
||||
|
||||
testClass<AbstractParcelizeIrBoxTest> {
|
||||
model("box", targetBackend = TargetBackend.JVM_IR)
|
||||
}
|
||||
|
||||
testClass<AbstractParcelizeBytecodeListingTest> {
|
||||
model("codegen", targetBackend = TargetBackend.JVM)
|
||||
}
|
||||
|
||||
testClass<AbstractParcelizeIrBytecodeListingTest> {
|
||||
model("codegen", targetBackend = TargetBackend.JVM_IR)
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("plugins/kapt3/kapt3-compiler/test", "plugins/kapt3/kapt3-compiler/testData") {
|
||||
testClass<AbstractClassFileToSourceStubConverterTest> {
|
||||
model("converter")
|
||||
|
||||
@@ -148,6 +148,10 @@ import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenc
|
||||
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
|
||||
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
|
||||
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
||||
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBoxTest
|
||||
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBytecodeListingTest
|
||||
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBoxTest
|
||||
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBytecodeListingTest
|
||||
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
|
||||
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
|
||||
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverTest
|
||||
@@ -1127,6 +1131,24 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("plugins/parcelize/parcelize-compiler/tests", "plugins/parcelize/parcelize-compiler/testData") {
|
||||
testClass<AbstractParcelizeBoxTest> {
|
||||
model("box", targetBackend = TargetBackend.JVM)
|
||||
}
|
||||
|
||||
testClass<AbstractParcelizeIrBoxTest> {
|
||||
model("box", targetBackend = TargetBackend.JVM_IR)
|
||||
}
|
||||
|
||||
testClass<AbstractParcelizeBytecodeListingTest> {
|
||||
model("codegen", targetBackend = TargetBackend.JVM)
|
||||
}
|
||||
|
||||
testClass<AbstractParcelizeIrBytecodeListingTest> {
|
||||
model("codegen", targetBackend = TargetBackend.JVM_IR)
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("plugins/kapt3/kapt3-compiler/test", "plugins/kapt3/kapt3-compiler/testData") {
|
||||
testClass<AbstractClassFileToSourceStubConverterTest> {
|
||||
model("converter")
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
description = "Parcelize compiler plugin"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
val robolectricClasspath by configurations.creating
|
||||
val parcelizeRuntimeForTests by configurations.creating
|
||||
|
||||
dependencies {
|
||||
testCompile(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
|
||||
compileOnly(project(":compiler:util"))
|
||||
compileOnly(project(":compiler:plugin-api"))
|
||||
compileOnly(project(":compiler:frontend"))
|
||||
compileOnly(project(":compiler:frontend.java"))
|
||||
compileOnly(project(":compiler:backend"))
|
||||
compileOnly(project(":compiler:ir.backend.common"))
|
||||
compileOnly(project(":compiler:backend.jvm"))
|
||||
compileOnly(project(":compiler:ir.tree.impl"))
|
||||
compileOnly(project(":plugins:parcelize:parcelize-runtime"))
|
||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
compileOnly(intellijDep()) { includeJars("asm-all", rootProject = rootProject) }
|
||||
|
||||
testCompile(project(":compiler:util"))
|
||||
testCompile(project(":compiler:backend"))
|
||||
testCompile(project(":compiler:ir.backend.common"))
|
||||
testCompile(project(":compiler:backend.jvm"))
|
||||
testCompile(project(":compiler:cli"))
|
||||
testCompile(project(":plugins:parcelize:parcelize-runtime"))
|
||||
testCompile(projectTests(":compiler:tests-common"))
|
||||
testCompile(project(":kotlin-test:kotlin-test-jvm"))
|
||||
testCompile(commonDep("junit:junit"))
|
||||
|
||||
testRuntime(intellijPluginDep("junit"))
|
||||
|
||||
robolectricClasspath(commonDep("org.robolectric", "robolectric"))
|
||||
robolectricClasspath("org.robolectric:android-all:4.4_r1-robolectric-1")
|
||||
robolectricClasspath(project(":plugins:parcelize:parcelize-runtime")) { isTransitive = false }
|
||||
|
||||
embedded(project(":plugins:parcelize:parcelize-runtime")) { isTransitive = false }
|
||||
|
||||
parcelizeRuntimeForTests(project(":plugins:parcelize:parcelize-runtime")) { isTransitive = false }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
javadocJar()
|
||||
sourcesJar()
|
||||
|
||||
testsJar()
|
||||
|
||||
projectTest {
|
||||
dependsOn(parcelizeRuntimeForTests)
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
useAndroidJar()
|
||||
doFirst {
|
||||
systemProperty("parcelizeRuntime.classpath", parcelizeRuntimeForTests.asPath)
|
||||
val androidPluginPath = File(intellijRootDir(), "plugins/android/lib").canonicalPath
|
||||
systemProperty("ideaSdk.androidPlugin.path", androidPluginPath)
|
||||
systemProperty("robolectric.classpath", robolectricClasspath.asPath)
|
||||
}
|
||||
}
|
||||
|
||||
apply(from = "$rootDir/gradle/kotlinPluginPublication.gradle.kts")
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.parcelize.ParcelizeComponentRegistrar
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.parcelize
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.TypeParceler
|
||||
import kotlinx.parcelize.WriteWith
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.parcelize.diagnostic.ErrorsParcelize
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
|
||||
class ParcelizeAnnotationChecker : CallChecker {
|
||||
companion object {
|
||||
val TYPE_PARCELER_FQNAME = FqName(TypeParceler::class.java.name)
|
||||
val WRITE_WITH_FQNAME = FqName(WriteWith::class.java.name)
|
||||
val IGNORED_ON_PARCEL_FQNAME = FqName(IgnoredOnParcel::class.java.name)
|
||||
}
|
||||
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
val constructorDescriptor = resolvedCall.resultingDescriptor as? ClassConstructorDescriptor ?: return
|
||||
val annotationClass = constructorDescriptor.constructedClass.takeIf { it.kind == ClassKind.ANNOTATION_CLASS } ?: return
|
||||
|
||||
val annotationEntry = resolvedCall.call.callElement.getNonStrictParentOfType<KtAnnotationEntry>() ?: return
|
||||
val annotationOwner = annotationEntry.getStrictParentOfType<KtModifierListOwner>() ?: return
|
||||
|
||||
if (annotationClass.fqNameSafe == TYPE_PARCELER_FQNAME) {
|
||||
checkTypeParcelerUsage(resolvedCall, annotationEntry, context, annotationOwner)
|
||||
}
|
||||
|
||||
if (annotationClass.fqNameSafe == WRITE_WITH_FQNAME) {
|
||||
checkWriteWithUsage(resolvedCall, annotationEntry, context, annotationOwner)
|
||||
}
|
||||
|
||||
if (annotationClass.fqNameSafe == IGNORED_ON_PARCEL_FQNAME) {
|
||||
checkIgnoredOnParcelUsage(annotationEntry, context, annotationOwner)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkIgnoredOnParcelUsage(annotationEntry: KtAnnotationEntry, context: CallCheckerContext, element: KtModifierListOwner) {
|
||||
if (element is KtParameter && PsiTreeUtil.getParentOfType(element, KtDeclaration::class.java) is KtPrimaryConstructor) {
|
||||
context.trace.report(ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY.on(annotationEntry))
|
||||
} else if (element !is KtProperty || PsiTreeUtil.getParentOfType(element, KtDeclaration::class.java) !is KtClass) {
|
||||
context.trace.report(ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL.on(annotationEntry))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkTypeParcelerUsage(
|
||||
resolvedCall: ResolvedCall<*>,
|
||||
annotationEntry: KtAnnotationEntry,
|
||||
context: CallCheckerContext,
|
||||
element: KtModifierListOwner
|
||||
) {
|
||||
val descriptor = context.trace[BindingContext.DECLARATION_TO_DESCRIPTOR, element] ?: return
|
||||
val thisMappedType = resolvedCall.typeArguments.values.takeIf { it.size == 2 }?.first() ?: return
|
||||
|
||||
val duplicatingAnnotationCount = descriptor.annotations
|
||||
.filter { it.fqName == TYPE_PARCELER_FQNAME }
|
||||
.mapNotNull { it.type.arguments.takeIf { args -> args.size == 2 }?.first()?.type }
|
||||
.count { it == thisMappedType }
|
||||
|
||||
if (duplicatingAnnotationCount > 1) {
|
||||
val reportElement = annotationEntry.typeArguments.firstOrNull() ?: annotationEntry
|
||||
context.trace.report(ErrorsParcelize.DUPLICATING_TYPE_PARCELERS.on(reportElement))
|
||||
return
|
||||
}
|
||||
|
||||
val containingClass = when (element) {
|
||||
is KtClassOrObject -> element
|
||||
is KtParameter -> element.containingClassOrObject
|
||||
else -> null
|
||||
}
|
||||
|
||||
checkIfTheContainingClassIsParcelize(containingClass, annotationEntry, context)
|
||||
|
||||
if (element is KtParameter && element.getStrictParentOfType<KtDeclaration>() is KtPrimaryConstructor) {
|
||||
val containingClassDescriptor = context.trace[BindingContext.CLASS, containingClass]
|
||||
val thisAnnotationDescriptor = context.trace[BindingContext.ANNOTATION, annotationEntry]
|
||||
|
||||
if (containingClass != null && containingClassDescriptor != null && thisAnnotationDescriptor != null) {
|
||||
// We can ignore value arguments here cause @TypeParceler is a zero-parameter annotation
|
||||
if (containingClassDescriptor.annotations.any { it.type == thisAnnotationDescriptor.type }) {
|
||||
val reportElement = (annotationEntry.typeReference?.typeElement as? KtUserType)?.referenceExpression ?: annotationEntry
|
||||
context.trace.report(ErrorsParcelize.REDUNDANT_TYPE_PARCELER.on(reportElement, containingClass))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkWriteWithUsage(
|
||||
resolvedCall: ResolvedCall<*>,
|
||||
annotationEntry: KtAnnotationEntry,
|
||||
context: CallCheckerContext,
|
||||
element: KtModifierListOwner
|
||||
) {
|
||||
if (element !is KtTypeReference) {
|
||||
return
|
||||
}
|
||||
|
||||
val actualType = context.trace[BindingContext.TYPE, element]?.replaceAnnotations(Annotations.EMPTY) ?: return
|
||||
|
||||
val parcelerType = resolvedCall.typeArguments.values.singleOrNull() ?: return
|
||||
val parcelerClass = parcelerType.constructor.declarationDescriptor as? ClassDescriptor ?: return
|
||||
|
||||
val containingClass = element.getStrictParentOfType<KtClassOrObject>()
|
||||
checkIfTheContainingClassIsParcelize(containingClass, annotationEntry, context)
|
||||
|
||||
fun reportElement() = annotationEntry.typeArguments.singleOrNull() ?: annotationEntry
|
||||
|
||||
if (parcelerClass.kind != ClassKind.OBJECT) {
|
||||
context.trace.report(ErrorsParcelize.PARCELER_SHOULD_BE_OBJECT.on(reportElement()))
|
||||
return
|
||||
}
|
||||
|
||||
fun KotlinType.fqName() = constructor.declarationDescriptor?.fqNameSafe
|
||||
val parcelerSuperType = parcelerClass.defaultType.supertypes().firstOrNull { it.fqName() == PARCELER_FQNAME } ?: return
|
||||
val expectedType = parcelerSuperType.arguments.singleOrNull()?.type ?: return
|
||||
|
||||
if (!actualType.isSubtypeOf(expectedType)) {
|
||||
context.trace.report(ErrorsParcelize.PARCELER_TYPE_INCOMPATIBLE.on(reportElement(), expectedType, actualType))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkIfTheContainingClassIsParcelize(
|
||||
containingClass: KtClassOrObject?,
|
||||
annotationEntry: KtAnnotationEntry,
|
||||
context: CallCheckerContext
|
||||
) {
|
||||
if (containingClass != null) {
|
||||
val containingClassDescriptor = context.trace[BindingContext.CLASS, containingClass]
|
||||
if (containingClassDescriptor != null && !containingClassDescriptor.isParcelize) {
|
||||
val reportElement = (annotationEntry.typeReference?.typeElement as? KtUserType)?.referenceExpression ?: annotationEntry
|
||||
context.trace.report(ErrorsParcelize.CLASS_SHOULD_BE_PARCELIZE.on(reportElement, containingClass))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <reified T : PsiElement> PsiElement.getStrictParentOfType(): T? {
|
||||
return PsiTreeUtil.getParentOfType(this, T::class.java, true)
|
||||
}
|
||||
|
||||
internal inline fun <reified T : PsiElement> PsiElement.getNonStrictParentOfType(): T? {
|
||||
return PsiTreeUtil.getParentOfType(this, T::class.java, false)
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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.parcelize
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
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.KtClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class ParcelizeClinitClassBuilderInterceptorExtension : 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(origin, 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 class AndroidOnDestroyCollectorClassBuilder(
|
||||
val declarationOrigin: JvmDeclarationOrigin,
|
||||
val delegateClassBuilder: ClassBuilder,
|
||||
val bindingContext: BindingContext
|
||||
) : DelegatingClassBuilder() {
|
||||
private var currentClass: KtClassOrObject? = 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>
|
||||
) {
|
||||
currentClass = origin as? KtClassOrObject
|
||||
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 && declarationOrigin.descriptor == descriptor && descriptor.isParcelize) {
|
||||
val baseVisitor = super.newMethod(JvmDeclarationOrigin.NO_ORIGIN, Opcodes.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
|
||||
|
||||
val descriptor = bindingContext[BindingContext.CLASS, currentClass]
|
||||
if (descriptor != null && declarationOrigin.descriptor == descriptor && descriptor.isParcelize) {
|
||||
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.API_VERSION, 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", "Landroid/os/Parcelable\$Creator;")
|
||||
}
|
||||
|
||||
super.visitInsn(opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
+439
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import kotlinx.parcelize.TypeParceler
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
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.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeResolveExtension.Companion.createMethod
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.*
|
||||
import org.jetbrains.kotlin.parcelize.serializers.*
|
||||
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase.Companion.FILE_DESCRIPTOR_FQNAME
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorFactory
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
open class ParcelizeCodegenExtension : ParcelizeExtensionBase, ExpressionCodegenExtension {
|
||||
open fun isAvailable(element: PsiElement): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override val shouldGenerateClassSyntheticPartsInLightClassesMode: Boolean
|
||||
get() = true
|
||||
|
||||
override fun generateClassSyntheticParts(codegen: ImplementationBodyCodegen) {
|
||||
val declaration = codegen.myClass as? PsiElement ?: return
|
||||
if (!isAvailable(declaration)) {
|
||||
return
|
||||
}
|
||||
|
||||
val parcelableClass = codegen.descriptor
|
||||
if (!parcelableClass.isParcelizeClassDescriptor) {
|
||||
return
|
||||
}
|
||||
|
||||
val propertiesToSerialize = getPropertiesToSerialize(codegen, parcelableClass)
|
||||
|
||||
val parcelerObject = parcelableClass.companionObjectDescriptor?.takeIf {
|
||||
TypeUtils.getAllSupertypes(it.defaultType).any { supertype -> supertype.isParceler }
|
||||
}
|
||||
|
||||
with(parcelableClass) {
|
||||
if (hasSyntheticDescribeContents()) {
|
||||
writeDescribeContentsFunction(codegen, propertiesToSerialize)
|
||||
}
|
||||
|
||||
if (hasSyntheticWriteToParcel()) {
|
||||
writeWriteToParcel(codegen, propertiesToSerialize, PARCEL_TYPE, parcelerObject)
|
||||
}
|
||||
|
||||
if (!hasCreatorField()) {
|
||||
writeCreatorAccessField(codegen, parcelableClass)
|
||||
}
|
||||
}
|
||||
|
||||
if (codegen.state.classBuilderMode != ClassBuilderMode.LIGHT_CLASSES) {
|
||||
val parcelClassType = ParcelizeResolveExtension.resolveParcelClassType(parcelableClass.module)
|
||||
?: error("Can't resolve 'android.os.Parcel' class")
|
||||
|
||||
val parcelableCreatorClassType = ParcelizeResolveExtension.resolveParcelableCreatorClassType(parcelableClass.module)
|
||||
?: error("Can't resolve 'android.os.Parcelable.Creator' class")
|
||||
|
||||
writeCreatorClass(
|
||||
codegen,
|
||||
parcelableClass, parcelClassType, parcelableCreatorClassType,
|
||||
PARCEL_TYPE, parcelerObject, propertiesToSerialize
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCompanionClassType(containerAsmType: Type, parcelerObject: ClassDescriptor): Pair<Type, String> {
|
||||
val shortName = parcelerObject.name
|
||||
return Pair(Type.getObjectType(containerAsmType.internalName + "\$$shortName"), shortName.asString())
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.writeWriteToParcel(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
properties: List<PropertyToSerialize>,
|
||||
parcelAsmType: Type,
|
||||
parcelerObject: ClassDescriptor?
|
||||
): Unit? {
|
||||
val containerAsmType = codegen.typeMapper.mapType(this.defaultType)
|
||||
|
||||
return findFunction(WRITE_TO_PARCEL)?.write(codegen) {
|
||||
if (parcelerObject != null) {
|
||||
val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject)
|
||||
|
||||
v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor)
|
||||
v.load(0, containerAsmType)
|
||||
v.load(1, PARCEL_TYPE)
|
||||
v.load(2, Type.INT_TYPE)
|
||||
v.invokevirtual(
|
||||
companionAsmType.internalName, "write",
|
||||
"(${containerAsmType.descriptor}${PARCEL_TYPE.descriptor}I)V", false
|
||||
)
|
||||
} else {
|
||||
val frameMap = FrameMap().apply {
|
||||
enterTemp(containerAsmType)
|
||||
enterTemp(PARCEL_TYPE)
|
||||
enterTemp(Type.INT_TYPE)
|
||||
}
|
||||
|
||||
val globalContext = ParcelSerializer.ParcelSerializerContext(codegen.typeMapper, containerAsmType, emptyList(), frameMap)
|
||||
|
||||
if (properties.isEmpty()) {
|
||||
val entityType = this@writeWriteToParcel.defaultType
|
||||
val asmType = codegen.state.typeMapper.mapType(entityType)
|
||||
val serializer = if (this@writeWriteToParcel.kind == ClassKind.CLASS) {
|
||||
NullAwareParcelSerializerWrapper(ZeroParameterClassSerializer(asmType, entityType))
|
||||
} else {
|
||||
ParcelSerializer.get(entityType, asmType, globalContext, strict = true)
|
||||
}
|
||||
|
||||
v.load(1, parcelAsmType)
|
||||
v.load(0, containerAsmType)
|
||||
serializer.writeValue(v)
|
||||
} else {
|
||||
for ((fieldName, type, parcelers) 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, globalContext.copy(typeParcelers = parcelers))
|
||||
serializer.writeValue(v)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
v.areturn(Type.VOID_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.writeDescribeContentsFunction(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
propertiesToSerialize: List<PropertyToSerialize>
|
||||
): Unit? {
|
||||
val hasFileDescriptorAnywhere = propertiesToSerialize.any { it.type.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() }
|
||||
}
|
||||
|
||||
data class PropertyToSerialize(val name: String, val type: KotlinType, val parcelers: List<TypeParcelerMapping>)
|
||||
|
||||
private fun getPropertiesToSerialize(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
parcelableClass: ClassDescriptor
|
||||
): List<PropertyToSerialize> {
|
||||
if (parcelableClass.kind != ClassKind.CLASS) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val constructor = parcelableClass.constructors.firstOrNull { it.isPrimary } ?: return emptyList()
|
||||
|
||||
val propertiesToSerialize = constructor.valueParameters.mapNotNull { param ->
|
||||
codegen.bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, param]
|
||||
}
|
||||
|
||||
val classParcelers = getTypeParcelers(parcelableClass.annotations)
|
||||
|
||||
return propertiesToSerialize.map {
|
||||
PropertyToSerialize(it.name.asString(), it.type, classParcelers + getTypeParcelers(it.annotations))
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeCreateFromParcel(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
parcelableClass: ClassDescriptor,
|
||||
parcelableCreatorClassType: KotlinType,
|
||||
creatorClass: ClassDescriptorImpl,
|
||||
parcelClassType: KotlinType,
|
||||
parcelAsmType: Type,
|
||||
parcelerObject: ClassDescriptor?,
|
||||
properties: List<PropertyToSerialize>
|
||||
) {
|
||||
val containerAsmType = codegen.typeMapper.mapType(parcelableClass)
|
||||
val creatorAsmType = codegen.typeMapper.mapType(creatorClass)
|
||||
|
||||
val overriddenFunction = parcelableCreatorClassType.findFunction(CREATE_FROM_PARCEL)
|
||||
?: error("Can't resolve 'android.os.Parcelable.Creator.${CREATE_FROM_PARCEL.methodName}' method")
|
||||
|
||||
createMethod(
|
||||
creatorClass, CREATE_FROM_PARCEL, Modality.FINAL,
|
||||
parcelableClass.defaultType, "in" to parcelClassType
|
||||
).write(codegen, overriddenFunction) {
|
||||
if (parcelerObject != null) {
|
||||
val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject)
|
||||
|
||||
v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor)
|
||||
v.load(1, PARCEL_TYPE)
|
||||
v.invokevirtual(companionAsmType.internalName, "create", "(${PARCEL_TYPE.descriptor})$containerAsmType", false)
|
||||
} else {
|
||||
v.anew(containerAsmType)
|
||||
v.dup()
|
||||
|
||||
val asmConstructorParameters = StringBuilder()
|
||||
val frameMap = FrameMap().apply {
|
||||
enterTemp(creatorAsmType)
|
||||
enterTemp(PARCEL_TYPE)
|
||||
}
|
||||
|
||||
val globalContext = ParcelSerializer.ParcelSerializerContext(codegen.typeMapper, containerAsmType, emptyList(), frameMap)
|
||||
|
||||
if (properties.isEmpty()) {
|
||||
val entityType = parcelableClass.defaultType
|
||||
val asmType = codegen.state.typeMapper.mapType(entityType)
|
||||
val serializer = if (parcelableClass.kind == ClassKind.CLASS) {
|
||||
NullAwareParcelSerializerWrapper(ZeroParameterClassSerializer(asmType, entityType))
|
||||
} else {
|
||||
ParcelSerializer.get(entityType, asmType, globalContext, strict = true)
|
||||
}
|
||||
v.load(1, parcelAsmType)
|
||||
serializer.readValue(v)
|
||||
} else {
|
||||
for ((_, type, parcelers) in properties) {
|
||||
val asmType = codegen.typeMapper.mapType(type)
|
||||
asmConstructorParameters.append(asmType.descriptor)
|
||||
|
||||
val serializer = ParcelSerializer.get(type, asmType, globalContext.copy(typeParcelers = parcelers))
|
||||
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 creatorType = Type.getObjectType("android/os/Parcelable\$Creator")
|
||||
val parcelableType = codegen.typeMapper.mapType(parcelableClass)
|
||||
val fieldSignature = "L${creatorType.internalName}<${parcelableType.descriptor}>;"
|
||||
|
||||
codegen.v.newField(
|
||||
JvmDeclarationOrigin.NO_ORIGIN,
|
||||
ACC_STATIC or ACC_PUBLIC or ACC_FINAL,
|
||||
"CREATOR",
|
||||
creatorType.descriptor,
|
||||
fieldSignature,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
private fun writeCreatorClass(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
parcelableClass: ClassDescriptor,
|
||||
parcelClassType: KotlinType,
|
||||
parcelableCreatorClassType: KotlinType,
|
||||
parcelAsmType: Type,
|
||||
parcelerObject: ClassDescriptor?,
|
||||
properties: List<PropertyToSerialize>
|
||||
) {
|
||||
val containerAsmType = codegen.typeMapper.mapType(parcelableClass.defaultType)
|
||||
val creatorAsmType = Type.getObjectType(containerAsmType.internalName + "\$Creator")
|
||||
|
||||
val creatorClass = ClassDescriptorImpl(
|
||||
parcelableClass, Name.identifier("Creator"), Modality.FINAL, ClassKind.CLASS, listOf(parcelableCreatorClassType),
|
||||
parcelableClass.source, false, LockBasedStorageManager.NO_LOCKS
|
||||
)
|
||||
|
||||
creatorClass.initialize(
|
||||
MemberScope.Empty, emptySet(),
|
||||
DescriptorFactory.createPrimaryConstructorForObject(creatorClass, creatorClass.source)
|
||||
)
|
||||
|
||||
val classBuilderForCreator = codegen.state.factory.newVisitor(
|
||||
JvmDeclarationOrigin(JvmDeclarationOriginKind.OTHER, null, creatorClass),
|
||||
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
|
||||
)
|
||||
|
||||
val classSignature = "Ljava/lang/Object;Landroid/os/Parcelable\$Creator<${containerAsmType.descriptor}>;"
|
||||
classBuilderForCreator.defineClass(
|
||||
null, V1_6, ACC_PUBLIC or ACC_FINAL or ACC_SUPER,
|
||||
creatorAsmType.internalName, classSignature, "java/lang/Object",
|
||||
arrayOf("android/os/Parcelable\$Creator")
|
||||
)
|
||||
|
||||
codegen.v.visitInnerClass(creatorAsmType.internalName, containerAsmType.internalName, "Creator", ACC_PUBLIC or ACC_STATIC)
|
||||
codegenForCreator.v.visitInnerClass(creatorAsmType.internalName, containerAsmType.internalName, "Creator", ACC_PUBLIC or ACC_STATIC)
|
||||
|
||||
writeSyntheticClassMetadata(classBuilderForCreator, codegen.state)
|
||||
|
||||
writeCreatorConstructor(codegenForCreator, creatorClass, creatorAsmType)
|
||||
writeNewArrayMethod(codegenForCreator, parcelableClass, parcelableCreatorClassType, creatorClass, parcelerObject)
|
||||
|
||||
writeCreateFromParcel(
|
||||
codegenForCreator,
|
||||
parcelableClass, parcelableCreatorClassType, creatorClass, parcelClassType,
|
||||
parcelAsmType, parcelerObject, 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,
|
||||
parcelableCreatorClassType: KotlinType,
|
||||
creatorClass: ClassDescriptorImpl,
|
||||
parcelerObject: ClassDescriptor?
|
||||
) {
|
||||
val builtIns = parcelableClass.builtIns
|
||||
val parcelableAsmType = codegen.typeMapper.mapType(parcelableClass)
|
||||
|
||||
val overriddenFunction = parcelableCreatorClassType.findFunction(NEW_ARRAY)
|
||||
?: error("Can't resolve 'android.os.Parcelable.Creator.${NEW_ARRAY.methodName}' method")
|
||||
|
||||
createMethod(
|
||||
creatorClass, NEW_ARRAY, Modality.FINAL,
|
||||
builtIns.getArrayType(Variance.INVARIANT, parcelableClass.defaultType),
|
||||
"size" to builtIns.intType
|
||||
).write(codegen, overriddenFunction) {
|
||||
if (parcelerObject != null) {
|
||||
val newArrayMethod = parcelerObject.unsubstitutedMemberScope
|
||||
.getContributedFunctions(Name.identifier("newArray"), NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS)
|
||||
.firstOrNull {
|
||||
it.typeParameters.isEmpty()
|
||||
&& it.kind == CallableMemberDescriptor.Kind.DECLARATION
|
||||
&& (it.valueParameters.size == 1 && KotlinBuiltIns.isInt(it.valueParameters[0].type))
|
||||
&& !((it.containingDeclaration as? ClassDescriptor)?.defaultType?.isParceler ?: true)
|
||||
}
|
||||
|
||||
if (newArrayMethod != null) {
|
||||
val containerAsmType = codegen.typeMapper.mapType(parcelableClass.defaultType)
|
||||
val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject)
|
||||
|
||||
v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor)
|
||||
v.load(1, Type.INT_TYPE)
|
||||
v.invokevirtual(companionAsmType.internalName, "newArray", "(I)[$parcelableAsmType", false)
|
||||
v.areturn(Type.getType("[$parcelableAsmType"))
|
||||
|
||||
return@write
|
||||
}
|
||||
}
|
||||
|
||||
v.load(1, Type.INT_TYPE)
|
||||
v.newarray(parcelableAsmType)
|
||||
v.areturn(Type.getType("[$parcelableAsmType"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.write(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
overriddenDescriptor: FunctionDescriptor? = null,
|
||||
code: ExpressionCodegen.() -> Unit
|
||||
) {
|
||||
val declarationOrigin = JvmDeclarationOrigin(JvmDeclarationOriginKind.OTHER, null, this)
|
||||
if (overriddenDescriptor != null) {
|
||||
this.overriddenDescriptors = listOf(overriddenDescriptor)
|
||||
}
|
||||
|
||||
codegen.functionCodegen.generateMethod(declarationOrigin, this, object : CodegenBased(codegen.state) {
|
||||
override fun doGenerateBody(e: ExpressionCodegen, signature: JvmMethodSignature) = with(e) {
|
||||
e.code()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun KotlinType.findFunction(componentKind: ParcelizeSyntheticComponent.ComponentKind): SimpleFunctionDescriptor? {
|
||||
return memberScope
|
||||
.getContributedFunctions(Name.identifier(componentKind.methodName), NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS)
|
||||
.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getTypeParcelers(annotations: Annotations): List<TypeParcelerMapping> {
|
||||
val typeParcelerFqName = FqName(TypeParceler::class.java.name)
|
||||
val serializers = mutableListOf<TypeParcelerMapping>()
|
||||
|
||||
for (annotation in annotations.filter { it.fqName == typeParcelerFqName }) {
|
||||
val (mappedType, parcelerType) = annotation.type.arguments.takeIf { it.size == 2 } ?: continue
|
||||
serializers += TypeParcelerMapping(mappedType.type, parcelerType.type)
|
||||
}
|
||||
|
||||
return serializers
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.container.StorageComponentContainer
|
||||
import org.jetbrains.kotlin.container.useInstance
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
|
||||
class ParcelizeComponentRegistrar : ComponentRegistrar {
|
||||
companion object {
|
||||
fun registerParcelizeComponents(project: Project) {
|
||||
ExpressionCodegenExtension.registerExtension(project, ParcelizeCodegenExtension())
|
||||
IrGenerationExtension.registerExtension(project, ParcelizeIrGeneratorExtension())
|
||||
SyntheticResolveExtension.registerExtension(project, ParcelizeResolveExtension())
|
||||
ClassBuilderInterceptorExtension.registerExtension(project, ParcelizeClinitClassBuilderInterceptorExtension())
|
||||
StorageComponentContainerContributor.registerExtension(project, ParcelizeDeclarationCheckerComponentContainerContributor())
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
||||
registerParcelizeComponents(project)
|
||||
}
|
||||
}
|
||||
|
||||
class ParcelizeDeclarationCheckerComponentContainerContributor : StorageComponentContainerContributor {
|
||||
override fun registerModuleComponents(
|
||||
container: StorageComponentContainer,
|
||||
platform: TargetPlatform,
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
) {
|
||||
if (platform.isJvm()) {
|
||||
container.useInstance(ParcelizeDeclarationChecker())
|
||||
container.useInstance(ParcelizeAnnotationChecker())
|
||||
}
|
||||
}
|
||||
}
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize
|
||||
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
||||
import org.jetbrains.kotlin.codegen.FrameMap
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.parcelize.diagnostic.ErrorsParcelize
|
||||
import org.jetbrains.kotlin.parcelize.serializers.ParcelSerializer
|
||||
import org.jetbrains.kotlin.parcelize.serializers.isParcelable
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmFieldAnnotation
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.isError
|
||||
|
||||
val ANDROID_PARCELABLE_CLASS_FQNAME = FqName("android.os.Parcelable")
|
||||
val ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME = FqName("android.os.Parcelable.Creator")
|
||||
val ANDROID_PARCEL_CLASS_FQNAME = FqName("android.os.Parcel")
|
||||
|
||||
class ParcelizeDeclarationChecker : DeclarationChecker {
|
||||
private companion object {
|
||||
private val IGNORED_ON_PARCEL_FQNAME = FqName(IgnoredOnParcel::class.java.canonicalName)
|
||||
}
|
||||
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
val trace = context.trace
|
||||
|
||||
when (descriptor) {
|
||||
is ClassDescriptor -> {
|
||||
checkParcelableClass(descriptor, declaration, trace, trace.bindingContext, context.languageVersionSettings)
|
||||
}
|
||||
is SimpleFunctionDescriptor -> {
|
||||
val containingClass = descriptor.containingDeclaration as? ClassDescriptor
|
||||
val ktFunction = declaration as? KtFunction
|
||||
if (containingClass != null && ktFunction != null) {
|
||||
checkParcelableClassMethod(descriptor, containingClass, ktFunction, trace)
|
||||
}
|
||||
}
|
||||
is PropertyDescriptor -> {
|
||||
val containingClass = descriptor.containingDeclaration as? ClassDescriptor
|
||||
val ktProperty = declaration as? KtProperty
|
||||
if (containingClass != null && ktProperty != null) {
|
||||
checkParcelableClassProperty(descriptor, containingClass, ktProperty, trace, trace.bindingContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkParcelableClassMethod(
|
||||
method: SimpleFunctionDescriptor,
|
||||
containingClass: ClassDescriptor,
|
||||
declaration: KtFunction,
|
||||
diagnosticHolder: DiagnosticSink
|
||||
) {
|
||||
if (!containingClass.isParcelize) {
|
||||
return
|
||||
}
|
||||
|
||||
if (method.isWriteToParcel() && declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
|
||||
val reportElement = declaration.modifierList?.getModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||
?: declaration.nameIdentifier
|
||||
?: declaration
|
||||
|
||||
diagnosticHolder.report(ErrorsParcelize.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED.on(reportElement))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkParcelableClassProperty(
|
||||
property: PropertyDescriptor,
|
||||
containingClass: ClassDescriptor,
|
||||
declaration: KtProperty,
|
||||
diagnosticHolder: DiagnosticSink,
|
||||
bindingContext: BindingContext
|
||||
) {
|
||||
fun hasIgnoredOnParcel(): Boolean {
|
||||
fun Annotations.hasIgnoredOnParcel() = any { it.fqName == IGNORED_ON_PARCEL_FQNAME }
|
||||
|
||||
return property.annotations.hasIgnoredOnParcel() || (property.getter?.annotations?.hasIgnoredOnParcel() ?: false)
|
||||
}
|
||||
|
||||
if (containingClass.isParcelize
|
||||
&& (declaration.hasDelegate() || bindingContext[BindingContext.BACKING_FIELD_REQUIRED, property] == true)
|
||||
&& !hasIgnoredOnParcel()
|
||||
) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.PROPERTY_WONT_BE_SERIALIZED.on(reportElement))
|
||||
}
|
||||
|
||||
// @JvmName is not applicable to property so we can check just the descriptor name
|
||||
if (property.name.asString() == "CREATOR" && property.findJvmFieldAnnotation() != null && containingClass.isCompanionObject) {
|
||||
val outerClass = containingClass.containingDeclaration as? ClassDescriptor
|
||||
if (outerClass != null && outerClass.isParcelize) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkParcelableClass(
|
||||
descriptor: ClassDescriptor,
|
||||
declaration: KtDeclaration,
|
||||
diagnosticHolder: DiagnosticSink,
|
||||
bindingContext: BindingContext,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
) {
|
||||
if (!descriptor.isParcelize) {
|
||||
return
|
||||
}
|
||||
|
||||
if (declaration !is KtClassOrObject) {
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_SHOULD_BE_CLASS.on(declaration))
|
||||
return
|
||||
}
|
||||
|
||||
if (declaration is KtClass && (declaration.isAnnotation() || declaration.isInterface())) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_SHOULD_BE_CLASS.on(reportElement))
|
||||
return
|
||||
}
|
||||
|
||||
for (companion in declaration.companionObjects) {
|
||||
if (companion.name == "CREATOR") {
|
||||
val reportElement = companion.nameIdentifier ?: companion
|
||||
diagnosticHolder.report(ErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement))
|
||||
}
|
||||
}
|
||||
|
||||
val sealedOrAbstract =
|
||||
declaration.modifierList?.let { it.getModifier(KtTokens.ABSTRACT_KEYWORD) ?: it.getModifier(KtTokens.SEALED_KEYWORD) }
|
||||
if (sealedOrAbstract != null) {
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_SHOULD_BE_INSTANTIABLE.on(sealedOrAbstract))
|
||||
}
|
||||
|
||||
if (declaration is KtClass && declaration.isInner()) {
|
||||
val reportElement = declaration.modifierList?.getModifier(KtTokens.INNER_KEYWORD) ?: declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_CANT_BE_INNER_CLASS.on(reportElement))
|
||||
}
|
||||
|
||||
if (declaration.isLocal) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_CANT_BE_LOCAL_CLASS.on(reportElement))
|
||||
}
|
||||
|
||||
val superTypes = TypeUtils.getAllSupertypes(descriptor.defaultType)
|
||||
if (superTypes.none { it.constructor.declarationDescriptor?.fqNameSafe == ANDROID_PARCELABLE_CLASS_FQNAME }) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.NO_PARCELABLE_SUPERTYPE.on(reportElement))
|
||||
}
|
||||
|
||||
for (supertypeEntry in declaration.superTypeListEntries) {
|
||||
supertypeEntry as? KtDelegatedSuperTypeEntry ?: continue
|
||||
val delegateExpression = supertypeEntry.delegateExpression ?: continue
|
||||
val type = bindingContext[BindingContext.TYPE, supertypeEntry.typeReference] ?: continue
|
||||
if (type.isParcelable()) {
|
||||
val reportElement = supertypeEntry.byKeywordNode?.psi ?: delegateExpression
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_DELEGATE_IS_NOT_ALLOWED.on(reportElement))
|
||||
}
|
||||
}
|
||||
|
||||
val primaryConstructor = declaration.primaryConstructor
|
||||
if (primaryConstructor == null && declaration.secondaryConstructors.isNotEmpty()) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR.on(reportElement))
|
||||
} else if (primaryConstructor != null && primaryConstructor.valueParameters.isEmpty()) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY.on(reportElement))
|
||||
}
|
||||
|
||||
val typeMapper = KotlinTypeMapper(
|
||||
bindingContext,
|
||||
ClassBuilderMode.FULL,
|
||||
descriptor.module.name.asString(),
|
||||
languageVersionSettings
|
||||
)
|
||||
|
||||
for (parameter in primaryConstructor?.valueParameters.orEmpty<KtParameter>()) {
|
||||
checkParcelableClassProperty(parameter, descriptor, diagnosticHolder, typeMapper)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkParcelableClassProperty(
|
||||
parameter: KtParameter,
|
||||
containerClass: ClassDescriptor,
|
||||
diagnosticHolder: DiagnosticSink,
|
||||
typeMapper: KotlinTypeMapper
|
||||
) {
|
||||
if (!parameter.hasValOrVar()) {
|
||||
val reportElement = parameter.nameIdentifier ?: parameter
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR.on(reportElement))
|
||||
}
|
||||
|
||||
val descriptor = typeMapper.bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter] ?: return
|
||||
val type = descriptor.type
|
||||
|
||||
if (!type.isError && !containerClass.hasCustomParceler()) {
|
||||
val asmType = typeMapper.mapType(type)
|
||||
|
||||
try {
|
||||
val parcelers = getTypeParcelers(descriptor.annotations) + getTypeParcelers(containerClass.annotations)
|
||||
val context = ParcelSerializer.ParcelSerializerContext(
|
||||
typeMapper,
|
||||
typeMapper.mapType(containerClass.defaultType),
|
||||
parcelers,
|
||||
FrameMap()
|
||||
)
|
||||
|
||||
ParcelSerializer.get(type, asmType, context, strict = true)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// get() throws IllegalArgumentException on unknown types
|
||||
val reportElement = parameter.typeReference ?: parameter.nameIdentifier ?: parameter
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_TYPE_NOT_SUPPORTED.on(reportElement))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.hasCustomParceler(): Boolean {
|
||||
val companionObjectSuperTypes = companionObjectDescriptor?.let { TypeUtils.getAllSupertypes(it.defaultType) } ?: return false
|
||||
return companionObjectSuperTypes.any { it.isParceler }
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.parcelize.ir.AndroidSymbols
|
||||
import org.jetbrains.kotlin.parcelize.ir.ParcelizeIrTransformer
|
||||
|
||||
class ParcelizeIrGeneratorExtension : IrGenerationExtension {
|
||||
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
|
||||
val arrayOfNulls = pluginContext.symbols.arrayOfNulls
|
||||
val charSequence = pluginContext.symbols.charSequence
|
||||
val androidSymbols = AndroidSymbols(pluginContext.irBuiltIns, arrayOfNulls, charSequence, moduleFragment)
|
||||
ParcelizeIrTransformer(pluginContext, androidSymbols).transform(moduleFragment)
|
||||
}
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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.parcelize
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import kotlinx.parcelize.Parceler
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
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.parcelize.ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
|
||||
open class ParcelizeResolveExtension : SyntheticResolveExtension {
|
||||
companion object {
|
||||
fun resolveParcelClassType(module: ModuleDescriptor): SimpleType? {
|
||||
return module.findClassAcrossModuleDependencies(ClassId.topLevel(FqName("android.os.Parcel")))?.defaultType
|
||||
}
|
||||
|
||||
fun resolveParcelableCreatorClassType(module: ModuleDescriptor): SimpleType? {
|
||||
val creatorClassId = ClassId(FqName("android.os"), FqName("Parcelable.Creator"), false)
|
||||
return module.findClassAcrossModuleDependencies(creatorClassId)?.defaultType
|
||||
}
|
||||
|
||||
fun createMethod(
|
||||
classDescriptor: ClassDescriptor,
|
||||
componentKind: ParcelizeSyntheticComponent.ComponentKind,
|
||||
modality: Modality,
|
||||
returnType: KotlinType,
|
||||
vararg parameters: Pair<String, KotlinType>
|
||||
): SimpleFunctionDescriptor {
|
||||
val functionDescriptor = object : ParcelizeSyntheticComponent, 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, DescriptorVisibilities.PUBLIC
|
||||
)
|
||||
|
||||
return functionDescriptor
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.makeValueParameter(name: String, type: KotlinType, index: Int): ValueParameterDescriptor {
|
||||
return ValueParameterDescriptorImpl(
|
||||
containingDeclaration = this,
|
||||
original = null,
|
||||
index = index,
|
||||
annotations = Annotations.EMPTY,
|
||||
name = Name.identifier(name),
|
||||
outType = type,
|
||||
declaresDefaultValue = false,
|
||||
isCrossinline = false,
|
||||
isNoinline = false,
|
||||
varargElementType = null,
|
||||
source = this.source
|
||||
)
|
||||
}
|
||||
|
||||
private val parcelizeMethodNames: List<Name> =
|
||||
listOf(Name.identifier(DESCRIBE_CONTENTS.methodName), Name.identifier(WRITE_TO_PARCEL.methodName))
|
||||
}
|
||||
|
||||
open fun isAvailable(element: PsiElement): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getSyntheticCompanionObjectNameIfNeeded(thisDescriptor: ClassDescriptor): Name? = null
|
||||
|
||||
override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List<Name> {
|
||||
return if (thisDescriptor.isParcelize) parcelizeMethodNames else emptyList()
|
||||
}
|
||||
|
||||
override fun generateSyntheticMethods(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
name: Name,
|
||||
bindingContext: BindingContext,
|
||||
fromSupertypes: List<SimpleFunctionDescriptor>,
|
||||
result: MutableCollection<SimpleFunctionDescriptor>
|
||||
) {
|
||||
fun isParcelizePluginEnabled(): Boolean {
|
||||
val sourceElement = (thisDescriptor.source as? PsiSourceElement)?.psi ?: return false
|
||||
return isAvailable(sourceElement)
|
||||
}
|
||||
|
||||
if (name.asString() == DESCRIBE_CONTENTS.methodName
|
||||
&& thisDescriptor.isParcelize
|
||||
&& isParcelizePluginEnabled()
|
||||
&& result.none { it.isDescribeContents() }
|
||||
&& fromSupertypes.none { it.isDescribeContents() }
|
||||
) {
|
||||
result += createMethod(thisDescriptor, DESCRIBE_CONTENTS, Modality.OPEN, thisDescriptor.builtIns.intType)
|
||||
} else if (name.asString() == WRITE_TO_PARCEL.methodName
|
||||
&& thisDescriptor.isParcelize
|
||||
&& isParcelizePluginEnabled()
|
||||
&& result.none { it.isWriteToParcel() }
|
||||
) {
|
||||
val builtIns = thisDescriptor.builtIns
|
||||
val parcelClassType = resolveParcelClassType(thisDescriptor.module) ?: ErrorUtils.createErrorType("Unresolved 'Parcel' type")
|
||||
result += createMethod(
|
||||
thisDescriptor, WRITE_TO_PARCEL, Modality.OPEN,
|
||||
builtIns.unitType, "parcel" to parcelClassType, "flags" to builtIns.intType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SimpleFunctionDescriptor.isDescribeContents(): Boolean {
|
||||
return this.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE
|
||||
&& modality != Modality.ABSTRACT
|
||||
&& typeParameters.isEmpty()
|
||||
&& valueParameters.isEmpty()
|
||||
// Unfortunately, we can't check the return type as it's unresolved in IDE light classes
|
||||
}
|
||||
}
|
||||
|
||||
internal fun SimpleFunctionDescriptor.isWriteToParcel(): Boolean {
|
||||
return typeParameters.isEmpty()
|
||||
&& valueParameters.size == 2
|
||||
// Unfortunately, we can't check the first parameter type as it's unresolved in IDE light classes
|
||||
&& KotlinBuiltIns.isInt(valueParameters[1].type)
|
||||
&& returnType?.let { KotlinBuiltIns.isUnit(it) } == true
|
||||
}
|
||||
|
||||
interface ParcelizeSyntheticComponent {
|
||||
val componentKind: ComponentKind
|
||||
|
||||
enum class ComponentKind(val methodName: String) {
|
||||
WRITE_TO_PARCEL("writeToParcel"),
|
||||
DESCRIBE_CONTENTS("describeContents"),
|
||||
NEW_ARRAY("newArray"),
|
||||
CREATE_FROM_PARCEL("createFromParcel")
|
||||
}
|
||||
}
|
||||
|
||||
val PARCELIZE_CLASS_FQNAME: FqName = FqName(Parcelize::class.java.canonicalName)
|
||||
internal val PARCELER_FQNAME: FqName = FqName(Parceler::class.java.canonicalName)
|
||||
|
||||
val ClassDescriptor.isParcelize: Boolean
|
||||
get() = this.annotations.hasAnnotation(PARCELIZE_CLASS_FQNAME)
|
||||
|
||||
val KotlinType.isParceler
|
||||
get() = constructor.declarationDescriptor?.fqNameSafe == PARCELER_FQNAME
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.parcelize.diagnostic
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers.RENDER_CLASS_OR_OBJECT
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers.RENDER_TYPE_WITH_ANNOTATIONS
|
||||
|
||||
object DefaultErrorMessagesParcelize : DefaultErrorMessages.Extension {
|
||||
private val MAP = DiagnosticFactoryToRendererMap("Parcelize")
|
||||
override fun getMap() = MAP
|
||||
|
||||
init {
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_SHOULD_BE_CLASS,
|
||||
"'Parcelable' should be a class"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_DELEGATE_IS_NOT_ALLOWED,
|
||||
"Delegating 'Parcelable' is not allowed"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_SHOULD_NOT_BE_ENUM_CLASS,
|
||||
"'Parcelable' should not be a 'enum class'"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_SHOULD_BE_INSTANTIABLE,
|
||||
"'Parcelable' should not be a 'sealed' or 'abstract' class"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_CANT_BE_INNER_CLASS,
|
||||
"'Parcelable' can't be an inner class"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_CANT_BE_LOCAL_CLASS,
|
||||
"'Parcelable' can't be a local class"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.NO_PARCELABLE_SUPERTYPE,
|
||||
"No 'Parcelable' supertype"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR,
|
||||
"'Parcelable' should have a primary constructor"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY,
|
||||
"The primary constructor is empty, no data will be serialized to 'Parcel'"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR,
|
||||
"'Parcelable' constructor parameter should be 'val' or 'var'"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PROPERTY_WONT_BE_SERIALIZED,
|
||||
"Property would not be serialized into a 'Parcel'. Add '@IgnoredOnParcel' annotation to remove the warning"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED,
|
||||
"Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED,
|
||||
"'CREATOR' definition is not allowed. Use 'Parceler' companion object instead"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_TYPE_NOT_SUPPORTED,
|
||||
"Type is not directly supported by 'Parcelize'. " +
|
||||
"Annotate the parameter type with '@RawValue' if you want it to be serialized using 'writeValue()'"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELER_SHOULD_BE_OBJECT,
|
||||
"Parceler should be an object"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELER_TYPE_INCOMPATIBLE,
|
||||
"Parceler type {0} is incompatible with {1}",
|
||||
RENDER_TYPE_WITH_ANNOTATIONS, RENDER_TYPE_WITH_ANNOTATIONS
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.DUPLICATING_TYPE_PARCELERS,
|
||||
"Duplicating ''TypeParceler'' annotations"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.REDUNDANT_TYPE_PARCELER,
|
||||
"This ''TypeParceler'' is already provided for {0}",
|
||||
RENDER_CLASS_OR_OBJECT
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.CLASS_SHOULD_BE_PARCELIZE,
|
||||
"{0} should be annotated with ''@Parcelize''",
|
||||
RENDER_CLASS_OR_OBJECT
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL,
|
||||
"'@IgnoredOnParcel' is only applicable to class properties"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY,
|
||||
"'@IgnoredOnParcel' is inapplicable to properties declared in the primary constructor"
|
||||
)
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.parcelize.diagnostic;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2;
|
||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
|
||||
import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
|
||||
import static org.jetbrains.kotlin.diagnostics.Severity.WARNING;
|
||||
|
||||
public interface ErrorsParcelize {
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_SHOULD_BE_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_DELEGATE_IS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_SHOULD_NOT_BE_ENUM_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_SHOULD_BE_INSTANTIABLE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_CANT_BE_INNER_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_CANT_BE_LOCAL_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> NO_PARCELABLE_SUPERTYPE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PROPERTY_WONT_BE_SERIALIZED = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> CREATOR_DEFINITION_IS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_TYPE_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELER_SHOULD_BE_OBJECT = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> PARCELER_TYPE_INCOMPATIBLE = DiagnosticFactory2.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> DUPLICATING_TYPE_PARCELERS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, KtClassOrObject> REDUNDANT_TYPE_PARCELER = DiagnosticFactory1.create(WARNING);
|
||||
DiagnosticFactory1<PsiElement, KtClassOrObject> CLASS_SHOULD_BE_PARCELIZE = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<PsiElement> INAPPLICABLE_IGNORED_ON_PARCEL = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
Object _initializer = new Object() {
|
||||
{
|
||||
Errors.Initializer.initializeFactoryNamesAndDefaultErrorMessages(ErrorsParcelize.class, DefaultErrorMessagesParcelize.INSTANCE);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.parcelize.ir
|
||||
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
|
||||
// An IR builder with access to AndroidSymbols and convenience methods to build calls to some of these methods.
|
||||
class AndroidIrBuilder internal constructor(
|
||||
val androidSymbols: AndroidSymbols,
|
||||
symbol: IrSymbol,
|
||||
startOffset: Int,
|
||||
endOffset: Int
|
||||
) : IrBuilderWithScope(IrGeneratorContextBase(androidSymbols.irBuiltIns), Scope(symbol), startOffset, endOffset) {
|
||||
fun parcelReadInt(receiver: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelReadInt).apply {
|
||||
dispatchReceiver = receiver
|
||||
}
|
||||
}
|
||||
|
||||
fun parcelReadParcelable(receiver: IrExpression, loader: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelReadParcelable).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, loader)
|
||||
}
|
||||
}
|
||||
|
||||
fun parcelReadString(receiver: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelReadString).apply {
|
||||
dispatchReceiver = receiver
|
||||
}
|
||||
}
|
||||
|
||||
fun parcelReadValue(receiver: IrExpression, loader: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelReadValue).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, loader)
|
||||
}
|
||||
}
|
||||
|
||||
fun parcelWriteInt(receiver: IrExpression, value: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelWriteInt).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, value)
|
||||
}
|
||||
}
|
||||
|
||||
fun parcelWriteParcelable(receiver: IrExpression, p: IrExpression, parcelableFlags: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelWriteParcelable).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, p)
|
||||
putValueArgument(1, parcelableFlags)
|
||||
}
|
||||
}
|
||||
|
||||
fun parcelWriteString(receiver: IrExpression, value: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelWriteString).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, value)
|
||||
}
|
||||
}
|
||||
|
||||
fun parcelWriteValue(receiver: IrExpression, v: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelWriteValue).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, v)
|
||||
}
|
||||
}
|
||||
|
||||
fun textUtilsWriteToParcel(cs: IrExpression, p: IrExpression, parcelableFlags: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.textUtilsWriteToParcel).apply {
|
||||
putValueArgument(0, cs)
|
||||
putValueArgument(1, p)
|
||||
putValueArgument(2, parcelableFlags)
|
||||
}
|
||||
}
|
||||
|
||||
fun classGetClassLoader(receiver: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.classGetClassLoader).apply {
|
||||
dispatchReceiver = receiver
|
||||
}
|
||||
}
|
||||
|
||||
fun getTextUtilsCharSequenceCreator(): IrExpression {
|
||||
return irGetField(null, androidSymbols.textUtilsCharSequenceCreator.owner)
|
||||
}
|
||||
}
|
||||
+467
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
// This file was autogenerated based on android.jar, do not edit it directly.
|
||||
package org.jetbrains.kotlin.parcelize.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.defaultType
|
||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||
import org.jetbrains.kotlin.ir.types.starProjectedType
|
||||
import org.jetbrains.kotlin.ir.types.typeWith
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
// All of the IR declarations needed by the parcelize plugin. Note that the declarations are generated based on JVM descriptors and
|
||||
// hence contain just enough information to produce correct JVM bytecode for *calls*. In particular, we omit generic types and
|
||||
// supertypes, which are not needed to produce correct bytecode.
|
||||
class AndroidSymbols(
|
||||
val irBuiltIns: IrBuiltIns,
|
||||
val arrayOfNulls: IrSimpleFunctionSymbol,
|
||||
private val charSequence: IrClassSymbol,
|
||||
private val moduleFragment: IrModuleFragment
|
||||
) {
|
||||
private val irFactory: IrFactory = IrFactoryImpl
|
||||
|
||||
private val javaIo: IrPackageFragment = createPackage("java.io")
|
||||
private val javaLang: IrPackageFragment = createPackage("java.lang")
|
||||
private val javaUtil: IrPackageFragment = createPackage("java.util")
|
||||
|
||||
private val kotlinJvm: IrPackageFragment = createPackage("kotlin.jvm")
|
||||
|
||||
private val androidOs: IrPackageFragment = createPackage("android.os")
|
||||
private val androidUtil: IrPackageFragment = createPackage("android.util")
|
||||
private val androidText: IrPackageFragment = createPackage("android.text")
|
||||
|
||||
private val androidOsBundle: IrClassSymbol =
|
||||
createClass(androidOs, "Bundle", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val androidOsIBinder: IrClassSymbol =
|
||||
createClass(androidOs, "IBinder", ClassKind.INTERFACE, Modality.ABSTRACT)
|
||||
|
||||
val androidOsParcel: IrClassSymbol =
|
||||
createClass(androidOs, "Parcel", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val androidOsParcelFileDescriptor: IrClassSymbol =
|
||||
createClass(androidOs, "ParcelFileDescriptor", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val androidOsParcelable: IrClassSymbol =
|
||||
createClass(androidOs, "Parcelable", ClassKind.INTERFACE, Modality.ABSTRACT)
|
||||
|
||||
private val androidOsPersistableBundle: IrClassSymbol =
|
||||
createClass(androidOs, "PersistableBundle", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val androidTextTextUtils: IrClassSymbol =
|
||||
createClass(androidText, "TextUtils", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val androidUtilSize: IrClassSymbol =
|
||||
createClass(androidUtil, "Size", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val androidUtilSizeF: IrClassSymbol =
|
||||
createClass(androidUtil, "SizeF", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val androidUtilSparseBooleanArray: IrClassSymbol =
|
||||
createClass(androidUtil, "SparseBooleanArray", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val javaIoFileDescriptor: IrClassSymbol =
|
||||
createClass(javaIo, "FileDescriptor", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val javaIoSerializable: IrClassSymbol =
|
||||
createClass(javaIo, "Serializable", ClassKind.INTERFACE, Modality.ABSTRACT)
|
||||
|
||||
val javaLangClass: IrClassSymbol =
|
||||
createClass(javaLang, "Class", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val javaLangClassLoader: IrClassSymbol =
|
||||
createClass(javaLang, "ClassLoader", ClassKind.CLASS, Modality.ABSTRACT)
|
||||
|
||||
private val javaUtilArrayList: IrClassSymbol =
|
||||
createClass(javaUtil, "ArrayList", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val javaUtilLinkedHashMap: IrClassSymbol =
|
||||
createClass(javaUtil, "LinkedHashMap", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val javaUtilLinkedHashSet: IrClassSymbol =
|
||||
createClass(javaUtil, "LinkedHashSet", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val javaUtilList: IrClassSymbol =
|
||||
createClass(javaUtil, "List", ClassKind.INTERFACE, Modality.ABSTRACT)
|
||||
|
||||
private val javaUtilTreeMap: IrClassSymbol =
|
||||
createClass(javaUtil, "TreeMap", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val javaUtilTreeSet: IrClassSymbol =
|
||||
createClass(javaUtil, "TreeSet", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
val androidOsParcelableCreator: IrClassSymbol = irFactory.buildClass {
|
||||
name = Name.identifier("Creator")
|
||||
kind = ClassKind.INTERFACE
|
||||
modality = Modality.ABSTRACT
|
||||
}.apply {
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
val t = addTypeParameter("T", irBuiltIns.anyNType)
|
||||
parent = androidOsParcelable.owner
|
||||
|
||||
addFunction("createFromParcel", t.defaultType, Modality.ABSTRACT).apply {
|
||||
addValueParameter("source", androidOsParcel.defaultType)
|
||||
}
|
||||
|
||||
addFunction(
|
||||
"newArray", irBuiltIns.arrayClass.typeWith(t.defaultType.makeNullable()),
|
||||
Modality.ABSTRACT
|
||||
).apply {
|
||||
addValueParameter("size", irBuiltIns.intType)
|
||||
}
|
||||
}.symbol
|
||||
|
||||
val kotlinKClassJava: IrPropertySymbol = irFactory.buildProperty {
|
||||
name = Name.identifier("java")
|
||||
}.apply {
|
||||
parent = kotlinJvm
|
||||
addGetter().apply {
|
||||
addExtensionReceiver(irBuiltIns.kClassClass.starProjectedType)
|
||||
returnType = javaLangClass.defaultType
|
||||
}
|
||||
}.symbol
|
||||
|
||||
val parcelCreateBinderArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("createBinderArray", irBuiltIns.arrayClass.typeWith(androidOsIBinder.defaultType)).symbol
|
||||
|
||||
val parcelCreateBinderArrayList: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("createBinderArrayList", javaUtilArrayList.defaultType).symbol
|
||||
|
||||
val parcelCreateBooleanArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createBooleanArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.booleanType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateByteArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createByteArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.byteType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateCharArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createCharArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.charType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateDoubleArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createDoubleArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.doubleType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateFloatArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createFloatArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.floatType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateIntArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createIntArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.intType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateLongArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createLongArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.longType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateStringArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("createStringArray", irBuiltIns.arrayClass.typeWith(irBuiltIns.stringType)).symbol
|
||||
|
||||
val parcelCreateStringArrayList: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("createStringArrayList", javaUtilArrayList.defaultType).symbol
|
||||
|
||||
val parcelReadBundle: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readBundle", androidOsBundle.defaultType).symbol
|
||||
|
||||
val parcelReadByte: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readByte", irBuiltIns.byteType).symbol
|
||||
|
||||
val parcelReadDouble: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readDouble", irBuiltIns.doubleType).symbol
|
||||
|
||||
val parcelReadFileDescriptor: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readFileDescriptor", androidOsParcelFileDescriptor.defaultType).symbol
|
||||
|
||||
val parcelReadFloat: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readFloat", irBuiltIns.floatType).symbol
|
||||
|
||||
val parcelReadInt: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readInt", irBuiltIns.intType).symbol
|
||||
|
||||
val parcelReadLong: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readLong", irBuiltIns.longType).symbol
|
||||
|
||||
val parcelReadParcelable: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readParcelable", androidOsParcelable.defaultType).apply {
|
||||
addValueParameter("loader", javaLangClassLoader.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelReadPersistableBundle: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readPersistableBundle", androidOsPersistableBundle.defaultType).symbol
|
||||
|
||||
val parcelReadSerializable: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readSerializable", javaIoSerializable.defaultType).symbol
|
||||
|
||||
val parcelReadSize: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readSize", androidUtilSize.defaultType).symbol
|
||||
|
||||
val parcelReadSizeF: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readSizeF", androidUtilSizeF.defaultType).symbol
|
||||
|
||||
val parcelReadSparseBooleanArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readSparseBooleanArray", androidUtilSparseBooleanArray.defaultType).symbol
|
||||
|
||||
val parcelReadString: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readString", irBuiltIns.stringType).symbol
|
||||
|
||||
val parcelReadStrongBinder: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readStrongBinder", androidOsIBinder.defaultType).symbol
|
||||
|
||||
val parcelReadValue: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readValue", irBuiltIns.anyNType).apply {
|
||||
addValueParameter("loader", javaLangClassLoader.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteBinderArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeBinderArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.arrayClass.typeWith(androidOsIBinder.defaultType))
|
||||
}.symbol
|
||||
|
||||
val parcelWriteBinderList: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeBinderList", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", javaUtilList.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteBooleanArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeBooleanArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.booleanType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteBundle: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeBundle", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", androidOsBundle.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteByte: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeByte", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.byteType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteByteArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeByteArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("b", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.byteType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteCharArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeCharArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.charType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteDouble: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeDouble", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.doubleType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteDoubleArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeDoubleArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.doubleType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteFileDescriptor: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeFileDescriptor", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", javaIoFileDescriptor.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteFloat: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeFloat", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.floatType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteFloatArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeFloatArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.floatType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteInt: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeInt", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.intType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteIntArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeIntArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.intType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteLong: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeLong", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.longType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteLongArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeLongArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.longType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteParcelable: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeParcelable", irBuiltIns.unitType).apply {
|
||||
addValueParameter("p", androidOsParcelable.defaultType)
|
||||
addValueParameter("parcelableFlags", irBuiltIns.intType)
|
||||
}.symbol
|
||||
|
||||
val parcelWritePersistableBundle: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writePersistableBundle", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", androidOsPersistableBundle.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteSerializable: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeSerializable", irBuiltIns.unitType).apply {
|
||||
addValueParameter("s", javaIoSerializable.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteSize: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeSize", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", androidUtilSize.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteSizeF: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeSizeF", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", androidUtilSizeF.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteSparseBooleanArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeSparseBooleanArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", androidUtilSparseBooleanArray.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteString: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeString", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.stringType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteStringArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeStringArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.arrayClass.typeWith(irBuiltIns.stringType))
|
||||
}.symbol
|
||||
|
||||
val parcelWriteStringList: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeStringList", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", javaUtilList.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteStrongBinder: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeStrongBinder", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", androidOsIBinder.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteValue: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeValue", irBuiltIns.unitType).apply {
|
||||
addValueParameter("v", irBuiltIns.anyNType)
|
||||
}.symbol
|
||||
|
||||
val textUtilsWriteToParcel: IrSimpleFunctionSymbol =
|
||||
androidTextTextUtils.owner.addFunction("writeToParcel", irBuiltIns.unitType, isStatic = true).apply {
|
||||
addValueParameter("cs", charSequence.defaultType)
|
||||
addValueParameter("p", androidOsParcel.defaultType)
|
||||
addValueParameter("parcelableFlags", irBuiltIns.intType)
|
||||
}.symbol
|
||||
|
||||
val classGetClassLoader: IrSimpleFunctionSymbol =
|
||||
javaLangClass.owner.addFunction("getClassLoader", javaLangClassLoader.defaultType).symbol
|
||||
|
||||
val arrayListConstructor: IrConstructorSymbol = javaUtilArrayList.owner.addConstructor().apply {
|
||||
addValueParameter("p_0", irBuiltIns.intType)
|
||||
}.symbol
|
||||
|
||||
val arrayListAdd: IrSimpleFunctionSymbol =
|
||||
javaUtilArrayList.owner.addFunction("add", irBuiltIns.booleanType).apply {
|
||||
addValueParameter("p_0", irBuiltIns.anyNType)
|
||||
}.symbol
|
||||
|
||||
val linkedHashMapConstructor: IrConstructorSymbol =
|
||||
javaUtilLinkedHashMap.owner.addConstructor().apply {
|
||||
addValueParameter("p_0", irBuiltIns.intType)
|
||||
}.symbol
|
||||
|
||||
val linkedHashMapPut: IrSimpleFunctionSymbol =
|
||||
javaUtilLinkedHashMap.owner.addFunction("put", irBuiltIns.anyNType).apply {
|
||||
addValueParameter("p_0", irBuiltIns.anyNType)
|
||||
addValueParameter("p_1", irBuiltIns.anyNType)
|
||||
}.symbol
|
||||
|
||||
val linkedHashSetConstructor: IrConstructorSymbol =
|
||||
javaUtilLinkedHashSet.owner.addConstructor().apply {
|
||||
addValueParameter("p_0", irBuiltIns.intType)
|
||||
}.symbol
|
||||
|
||||
val linkedHashSetAdd: IrSimpleFunctionSymbol =
|
||||
javaUtilLinkedHashSet.owner.addFunction("add", irBuiltIns.booleanType).apply {
|
||||
addValueParameter("p_0", irBuiltIns.anyNType)
|
||||
}.symbol
|
||||
|
||||
val treeMapConstructor: IrConstructorSymbol = javaUtilTreeMap.owner.addConstructor().symbol
|
||||
|
||||
val treeMapPut: IrSimpleFunctionSymbol =
|
||||
javaUtilTreeMap.owner.addFunction("put", irBuiltIns.anyNType).apply {
|
||||
addValueParameter("p_0", irBuiltIns.anyNType)
|
||||
addValueParameter("p_1", irBuiltIns.anyNType)
|
||||
}.symbol
|
||||
|
||||
val treeSetConstructor: IrConstructorSymbol = javaUtilTreeSet.owner.addConstructor().symbol
|
||||
|
||||
val treeSetAdd: IrSimpleFunctionSymbol =
|
||||
javaUtilTreeSet.owner.addFunction("add", irBuiltIns.booleanType).apply {
|
||||
addValueParameter("p_0", irBuiltIns.anyNType)
|
||||
}.symbol
|
||||
|
||||
val textUtilsCharSequenceCreator: IrFieldSymbol = androidTextTextUtils.owner.addField {
|
||||
name = Name.identifier("CHAR_SEQUENCE_CREATOR")
|
||||
type = androidOsParcelableCreator.defaultType
|
||||
isStatic = true
|
||||
}.symbol
|
||||
|
||||
private fun createPackage(packageName: String): IrPackageFragment =
|
||||
IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment(
|
||||
moduleFragment.descriptor,
|
||||
FqName(packageName)
|
||||
)
|
||||
|
||||
private fun createClass(
|
||||
irPackage: IrPackageFragment,
|
||||
shortName: String,
|
||||
classKind: ClassKind,
|
||||
classModality: Modality
|
||||
): IrClassSymbol = irFactory.buildClass {
|
||||
name = Name.identifier(shortName)
|
||||
kind = classKind
|
||||
modality = classModality
|
||||
}.apply {
|
||||
parent = irPackage
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
}.symbol
|
||||
|
||||
fun createBuilder(
|
||||
symbol: IrSymbol,
|
||||
startOffset: Int = UNDEFINED_OFFSET,
|
||||
endOffset: Int = UNDEFINED_OFFSET
|
||||
) = AndroidIrBuilder(this, symbol, startOffset, endOffset)
|
||||
}
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.psiElement
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.parcelize.serializers.RAW_VALUE_ANNOTATION_FQNAME
|
||||
|
||||
class IrParcelSerializerFactory(symbols: AndroidSymbols) {
|
||||
/**
|
||||
* Resolve the given [irType] to a corresponding [IrParcelSerializer]. This depends on the TypeParcelers which
|
||||
* are currently in [scope], as well as the type of the enclosing Parceleable class [parcelizeType], which is needed
|
||||
* to get a class loader for reflection based serialization. Beyond this, we need to know whether to allow
|
||||
* using read/writeValue for serialization (if [strict] is false). Beyond this, we need to know whether we are
|
||||
* producing parcelers for properties of a Parcelable (if [toplevel] is true), or for a complete Parcelable.
|
||||
*/
|
||||
fun get(
|
||||
irType: IrType,
|
||||
scope: IrParcelerScope?,
|
||||
parcelizeType: IrType,
|
||||
strict: Boolean = false,
|
||||
toplevel: Boolean = false
|
||||
): IrParcelSerializer {
|
||||
fun strict() = strict && !irType.hasAnnotation(RAW_VALUE_ANNOTATION_FQNAME)
|
||||
|
||||
scope.getCustomSerializer(irType)?.let { parceler ->
|
||||
return IrCustomParcelSerializer(parceler)
|
||||
}
|
||||
|
||||
// TODO inline classes
|
||||
val classifier = irType.erasedUpperBound
|
||||
val classifierFqName = classifier.fqNameWhenAvailable?.asString()
|
||||
when (classifierFqName) {
|
||||
// Built-in parcel serializers
|
||||
"kotlin.String", "java.lang.String" ->
|
||||
return stringSerializer
|
||||
"kotlin.CharSequence", "java.lang.CharSequence" ->
|
||||
return charSequenceSerializer
|
||||
"android.os.Bundle" ->
|
||||
return bundleSerializer
|
||||
"android.os.PersistableBundle" ->
|
||||
return persistableBundleSerializer
|
||||
|
||||
// Non-nullable built-in serializers
|
||||
"kotlin.Byte", "java.lang.Byte" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, byteSerializer)
|
||||
"kotlin.Boolean", "java.lang.Boolean" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, booleanSerializer)
|
||||
"kotlin.Char", "java.lang.Character" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, charSerializer)
|
||||
"kotlin.Short", "java.lang.Short" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, shortSerializer)
|
||||
"kotlin.Int", "java.lang.Integer" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, intSerializer)
|
||||
"kotlin.Long", "java.lang.Long" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, longSerializer)
|
||||
"kotlin.Float", "java.lang.Float" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, floatSerializer)
|
||||
"kotlin.Double", "java.lang.Double" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, doubleSerializer)
|
||||
"java.io.FileDescriptor" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, fileDescriptorSerializer)
|
||||
"android.util.Size" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, sizeSerializer)
|
||||
"android.util.SizeF" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, sizeFSerializer)
|
||||
|
||||
// Built-in non-parameterized container types.
|
||||
"kotlin.IntArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.intType))
|
||||
return intArraySerializer
|
||||
"kotlin.BooleanArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.booleanType))
|
||||
return booleanArraySerializer
|
||||
"kotlin.ByteArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.byteType))
|
||||
return byteArraySerializer
|
||||
"kotlin.CharArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.charType))
|
||||
return charArraySerializer
|
||||
"kotlin.FloatArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.floatType))
|
||||
return floatArraySerializer
|
||||
"kotlin.DoubleArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.doubleType))
|
||||
return doubleArraySerializer
|
||||
"kotlin.LongArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.longType))
|
||||
return longArraySerializer
|
||||
"android.util.SparseBooleanArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.booleanType))
|
||||
return sparseBooleanArraySerializer
|
||||
}
|
||||
|
||||
// Generic container types
|
||||
when (classifierFqName) {
|
||||
// Apart from kotlin.Array and kotlin.ShortArray, these will only be hit if we have a
|
||||
// special parceler for the element type.
|
||||
"kotlin.Array", "kotlin.ShortArray", "kotlin.IntArray",
|
||||
"kotlin.BooleanArray", "kotlin.ByteArray", "kotlin.CharArray",
|
||||
"kotlin.FloatArray", "kotlin.DoubleArray", "kotlin.LongArray" -> {
|
||||
val elementType = irType.getArrayElementType(irBuiltIns)
|
||||
|
||||
if (!scope.hasCustomSerializer(elementType)) {
|
||||
when (elementType.erasedUpperBound.fqNameWhenAvailable?.asString()) {
|
||||
"java.lang.String", "kotlin.String" ->
|
||||
return stringArraySerializer
|
||||
"android.os.IBinder" ->
|
||||
return iBinderArraySerializer
|
||||
}
|
||||
}
|
||||
|
||||
val arrayType =
|
||||
if (classifier.defaultType.isPrimitiveArray()) classifier.defaultType else irBuiltIns.arrayClass.typeWith(elementType)
|
||||
return IrArrayParcelSerializer(arrayType, elementType, get(elementType, scope, parcelizeType, strict()))
|
||||
}
|
||||
|
||||
// This will only be hit if we have a custom serializer for booleans
|
||||
"android.util.SparseBooleanArray" ->
|
||||
return IrSparseArrayParcelSerializer(
|
||||
classifier,
|
||||
irBuiltIns.booleanType,
|
||||
get(irBuiltIns.booleanType, scope, parcelizeType, strict())
|
||||
)
|
||||
"android.util.SparseIntArray" ->
|
||||
return IrSparseArrayParcelSerializer(
|
||||
classifier,
|
||||
irBuiltIns.intType,
|
||||
get(irBuiltIns.intType, scope, parcelizeType, strict())
|
||||
)
|
||||
"android.util.SparseLongArray" ->
|
||||
return IrSparseArrayParcelSerializer(
|
||||
classifier,
|
||||
irBuiltIns.longType,
|
||||
get(irBuiltIns.longType, scope, parcelizeType, strict())
|
||||
)
|
||||
"android.util.SparseArray" -> {
|
||||
val elementType = (irType as IrSimpleType).arguments.single().upperBound(irBuiltIns)
|
||||
return IrSparseArrayParcelSerializer(classifier, elementType, get(elementType, scope, parcelizeType, strict()))
|
||||
}
|
||||
|
||||
// TODO: More java collections?
|
||||
// TODO: Add tests for all of these types, not just some common ones...
|
||||
// FIXME: Is the support for ArrayDeque missing in the old BE?
|
||||
"kotlin.collections.MutableList", "kotlin.collections.List", "java.util.List",
|
||||
"kotlin.collections.ArrayList", "java.util.ArrayList",
|
||||
"kotlin.collections.ArrayDeque", "java.util.ArrayDeque",
|
||||
"kotlin.collections.MutableSet", "kotlin.collections.Set", "java.util.Set",
|
||||
"kotlin.collections.HashSet", "java.util.HashSet",
|
||||
"kotlin.collections.LinkedHashSet", "java.util.LinkedHashSet",
|
||||
"java.util.NavigableSet", "java.util.SortedSet" -> {
|
||||
val elementType = (irType as IrSimpleType).arguments.single().upperBound(irBuiltIns)
|
||||
if (!scope.hasCustomSerializer(elementType) && classifierFqName in setOf(
|
||||
"kotlin.collections.List", "kotlin.collections.MutableList", "kotlin.collections.ArrayList",
|
||||
"java.util.List", "java.util.ArrayList"
|
||||
)
|
||||
) {
|
||||
when (elementType.erasedUpperBound.fqNameWhenAvailable?.asString()) {
|
||||
"android.os.IBinder" ->
|
||||
return iBinderListSerializer
|
||||
"kotlin.String", "java.lang.String" ->
|
||||
return stringListSerializer
|
||||
}
|
||||
}
|
||||
return wrapNullableSerializerIfNeeded(
|
||||
irType,
|
||||
IrListParcelSerializer(classifier, elementType, get(elementType, scope, parcelizeType, strict()))
|
||||
)
|
||||
}
|
||||
|
||||
"kotlin.collections.MutableMap", "kotlin.collections.Map", "java.util.Map",
|
||||
"kotlin.collections.HashMap", "java.util.HashMap",
|
||||
"kotlin.collections.LinkedHashMap", "java.util.LinkedHashMap",
|
||||
"java.util.SortedMap", "java.util.NavigableMap", "java.util.TreeMap",
|
||||
"java.util.concurrent.ConcurrentHashMap" -> {
|
||||
val keyType = (irType as IrSimpleType).arguments[0].upperBound(irBuiltIns)
|
||||
val valueType = irType.arguments[1].upperBound(irBuiltIns)
|
||||
val parceler =
|
||||
IrMapParcelSerializer(
|
||||
classifier,
|
||||
keyType,
|
||||
valueType,
|
||||
get(keyType, scope, parcelizeType, strict()),
|
||||
get(valueType, scope, parcelizeType, strict())
|
||||
)
|
||||
return wrapNullableSerializerIfNeeded(irType, parceler)
|
||||
}
|
||||
}
|
||||
|
||||
// Generic parcelable types
|
||||
when {
|
||||
classifier.isSubclassOfFqName("android.os.Parcelable")
|
||||
// Avoid infinite loops when deriving parcelers for enum or object classes.
|
||||
&& !(toplevel && (classifier.isObject || classifier.isEnumClass)) -> {
|
||||
// We try to use writeToParcel/createFromParcel directly whenever possible, but there are some caveats.
|
||||
//
|
||||
// According to the JLS, changing a class from final to non-final is a binary compatible change, hence we
|
||||
// cannot use the writeToParcel/createFromParcel methods directly when serializing classes from external
|
||||
// dependencies. This issue was originally reported in KT-20029.
|
||||
//
|
||||
// Conversely, we can and should apply this optimization to all classes in the current module which
|
||||
// implement Parcelable (KT-20030). There are two cases to consider. If the class is annotated with
|
||||
// @Parcelize, we will create the corresponding methods/fields ourselves, before we generate the code
|
||||
// for writeToParcel/createFromParcel. For Java classes (or compiled Kotlin classes annotated with
|
||||
// @Parcelize), we'll have a field in the class itself. Finally, with Parcelable instances which were
|
||||
// manually implemented in Kotlin, we'll instead have an @JvmField property getter in the companion object.
|
||||
return if (classifier.modality == Modality.FINAL && classifier.psiElement != null
|
||||
&& (classifier.isParcelize || classifier.hasCreatorField)
|
||||
) {
|
||||
wrapNullableSerializerIfNeeded(irType, IrEfficientParcelableParcelSerializer(classifier))
|
||||
} else {
|
||||
// In all other cases, we have to use the generic methods in Parcel, which use reflection internally.
|
||||
IrGenericParcelableParcelSerializer(parcelizeType)
|
||||
}
|
||||
}
|
||||
|
||||
classifier.isSubclassOfFqName("android.os.IBinder") ->
|
||||
return iBinderSerializer
|
||||
|
||||
classifier.isObject ->
|
||||
return IrObjectParcelSerializer(classifier)
|
||||
|
||||
classifier.isEnumClass ->
|
||||
return wrapNullableSerializerIfNeeded(irType, IrEnumParcelSerializer(classifier))
|
||||
|
||||
classifier.isSubclassOfFqName("java.io.Serializable")
|
||||
// Functions and Continuations are always serializable.
|
||||
|| irType.isFunctionTypeOrSubtype() || irType.isSuspendFunctionTypeOrSubtype() ->
|
||||
return serializableSerializer
|
||||
|
||||
strict() ->
|
||||
throw IllegalArgumentException("Illegal type, could not find a specific serializer for ${irType.render()}")
|
||||
|
||||
else ->
|
||||
return IrGenericValueParcelSerializer(parcelizeType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapNullableSerializerIfNeeded(irType: IrType, serializer: IrParcelSerializer) =
|
||||
if (irType.isNullable()) IrNullAwareParcelSerializer(serializer) else serializer
|
||||
|
||||
private val irBuiltIns: IrBuiltIns = symbols.irBuiltIns
|
||||
|
||||
private val stringArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateStringArray, symbols.parcelWriteStringArray)
|
||||
private val stringListSerializer = IrSimpleParcelSerializer(symbols.parcelCreateStringArrayList, symbols.parcelWriteStringList)
|
||||
private val iBinderSerializer = IrSimpleParcelSerializer(symbols.parcelReadStrongBinder, symbols.parcelWriteStrongBinder)
|
||||
private val iBinderArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateBinderArray, symbols.parcelWriteBinderArray)
|
||||
private val iBinderListSerializer = IrSimpleParcelSerializer(symbols.parcelCreateBinderArrayList, symbols.parcelWriteBinderList)
|
||||
private val serializableSerializer = IrSimpleParcelSerializer(symbols.parcelReadSerializable, symbols.parcelWriteSerializable)
|
||||
private val stringSerializer = IrSimpleParcelSerializer(symbols.parcelReadString, symbols.parcelWriteString)
|
||||
private val byteSerializer = IrSimpleParcelSerializer(symbols.parcelReadByte, symbols.parcelWriteByte)
|
||||
private val intSerializer = IrSimpleParcelSerializer(symbols.parcelReadInt, symbols.parcelWriteInt)
|
||||
private val longSerializer = IrSimpleParcelSerializer(symbols.parcelReadLong, symbols.parcelWriteLong)
|
||||
private val floatSerializer = IrSimpleParcelSerializer(symbols.parcelReadFloat, symbols.parcelWriteFloat)
|
||||
private val doubleSerializer = IrSimpleParcelSerializer(symbols.parcelReadDouble, symbols.parcelWriteDouble)
|
||||
private val intArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateIntArray, symbols.parcelWriteIntArray)
|
||||
private val booleanArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateBooleanArray, symbols.parcelWriteBooleanArray)
|
||||
private val byteArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateByteArray, symbols.parcelWriteByteArray)
|
||||
private val charArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateCharArray, symbols.parcelWriteCharArray)
|
||||
private val doubleArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateDoubleArray, symbols.parcelWriteDoubleArray)
|
||||
private val floatArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateFloatArray, symbols.parcelWriteFloatArray)
|
||||
private val longArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateLongArray, symbols.parcelWriteLongArray)
|
||||
|
||||
// Primitive types without dedicated read/write methods need an additional cast.
|
||||
private val booleanSerializer = IrWrappedIntParcelSerializer(irBuiltIns.booleanType)
|
||||
private val shortSerializer = IrWrappedIntParcelSerializer(irBuiltIns.shortType)
|
||||
private val charSerializer = IrWrappedIntParcelSerializer(irBuiltIns.charType)
|
||||
|
||||
private val charSequenceSerializer = IrCharSequenceParcelSerializer()
|
||||
|
||||
// TODO The old backend uses the hidden "read/writeRawFileDescriptor" methods.
|
||||
private val fileDescriptorSerializer = IrSimpleParcelSerializer(symbols.parcelReadFileDescriptor, symbols.parcelWriteFileDescriptor)
|
||||
|
||||
private val sizeSerializer = IrSimpleParcelSerializer(symbols.parcelReadSize, symbols.parcelWriteSize)
|
||||
private val sizeFSerializer = IrSimpleParcelSerializer(symbols.parcelReadSizeF, symbols.parcelWriteSizeF)
|
||||
private val bundleSerializer = IrSimpleParcelSerializer(symbols.parcelReadBundle, symbols.parcelWriteBundle)
|
||||
private val persistableBundleSerializer =
|
||||
IrSimpleParcelSerializer(symbols.parcelReadPersistableBundle, symbols.parcelWritePersistableBundle)
|
||||
private val sparseBooleanArraySerializer =
|
||||
IrSimpleParcelSerializer(symbols.parcelReadSparseBooleanArray, symbols.parcelWriteSparseBooleanArray)
|
||||
}
|
||||
+536
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase.Companion.CREATOR_NAME
|
||||
|
||||
interface IrParcelSerializer {
|
||||
fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression
|
||||
fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression
|
||||
}
|
||||
|
||||
fun AndroidIrBuilder.readParcelWith(serializer: IrParcelSerializer, parcel: IrValueDeclaration): IrExpression {
|
||||
return with(serializer) { readParcel(parcel) }
|
||||
}
|
||||
|
||||
fun AndroidIrBuilder.writeParcelWith(
|
||||
serializer: IrParcelSerializer,
|
||||
parcel: IrValueDeclaration,
|
||||
flags: IrValueDeclaration,
|
||||
value: IrExpression
|
||||
): IrExpression {
|
||||
return with(serializer) { writeParcel(parcel, flags, value) }
|
||||
}
|
||||
|
||||
// Creates a serializer from a pair of parcel methods of the form reader()T and writer(T)V.
|
||||
class IrSimpleParcelSerializer(private val reader: IrSimpleFunctionSymbol, private val writer: IrSimpleFunctionSymbol) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return irCall(reader).apply { dispatchReceiver = irGet(parcel) }
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return irCall(writer).apply {
|
||||
dispatchReceiver = irGet(parcel)
|
||||
putValueArgument(0, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize a value of the primitive [parcelType] by coercion to int.
|
||||
class IrWrappedIntParcelSerializer(private val parcelType: IrType) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return if (parcelType.isBoolean()) {
|
||||
irNotEquals(parcelReadInt(irGet(parcel)), irInt(0))
|
||||
} else {
|
||||
val conversion = context.irBuiltIns.intClass.functions.first { function ->
|
||||
function.owner.name.asString() == "to${parcelType.getClass()!!.name}"
|
||||
}
|
||||
irCall(conversion).apply {
|
||||
dispatchReceiver = parcelReadInt(irGet(parcel))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression =
|
||||
parcelWriteInt(
|
||||
irGet(parcel),
|
||||
if (parcelType.isBoolean()) {
|
||||
irIfThenElse(context.irBuiltIns.intType, value, irInt(1), irInt(0))
|
||||
} else {
|
||||
val conversion = parcelType.classOrNull!!.functions.first { function ->
|
||||
function.owner.name.asString() == "toInt"
|
||||
}
|
||||
irCall(conversion).apply { dispatchReceiver = value }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Wraps a non-null aware parceler to handle nullable types.
|
||||
class IrNullAwareParcelSerializer(private val serializer: IrParcelSerializer) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
val nonNullResult = readParcelWith(serializer, parcel)
|
||||
return irIfThenElse(
|
||||
nonNullResult.type.makeNullable(),
|
||||
irEquals(parcelReadInt(irGet(parcel)), irInt(0)),
|
||||
irNull(),
|
||||
nonNullResult
|
||||
)
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return irLetS(value) { irValueSymbol ->
|
||||
irIfNull(
|
||||
context.irBuiltIns.unitType,
|
||||
irGet(irValueSymbol.owner),
|
||||
parcelWriteInt(irGet(parcel), irInt(0)),
|
||||
irBlock {
|
||||
+parcelWriteInt(irGet(parcel), irInt(1))
|
||||
+writeParcelWith(serializer, parcel, flags, irGet(irValueSymbol.owner))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for object classes. We avoid empty parcels by writing a dummy value. Not null-safe.
|
||||
class IrObjectParcelSerializer(private val objectClass: IrClass) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression
|
||||
// Avoid empty parcels
|
||||
{
|
||||
return irBlock {
|
||||
+parcelReadInt(irGet(parcel))
|
||||
+irGetObject(objectClass.symbol)
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return parcelWriteInt(irGet(parcel), irInt(1))
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for classes with a default constructor. We avoid empty parcels by writing a dummy value. Not null-safe.
|
||||
class IrNoParameterClassParcelSerializer(private val irClass: IrClass) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
val defaultConstructor = irClass.primaryConstructor!!
|
||||
return irBlock {
|
||||
+parcelReadInt(irGet(parcel))
|
||||
+irCall(defaultConstructor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return parcelWriteInt(irGet(parcel), irInt(1))
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for enum classes. Not null-safe.
|
||||
class IrEnumParcelSerializer(enumClass: IrClass) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return irCall(enumValueOf).apply {
|
||||
putValueArgument(0, parcelReadString(irGet(parcel)))
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return parcelWriteString(irGet(parcel), irCall(enumName).apply {
|
||||
dispatchReceiver = value
|
||||
})
|
||||
}
|
||||
|
||||
private val enumValueOf: IrFunctionSymbol =
|
||||
enumClass.functions.single { function ->
|
||||
function.name.asString() == "valueOf" && function.dispatchReceiverParameter == null
|
||||
&& function.extensionReceiverParameter == null && function.valueParameters.size == 1
|
||||
&& function.valueParameters.single().type.isString()
|
||||
}.symbol
|
||||
|
||||
private val enumName: IrFunctionSymbol = enumClass.getPropertyGetter("name")!!
|
||||
}
|
||||
|
||||
// Parcel serializer for the java CharSequence interface.
|
||||
class IrCharSequenceParcelSerializer : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return parcelableCreatorCreateFromParcel(getTextUtilsCharSequenceCreator(), irGet(parcel))
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return textUtilsWriteToParcel(value, irGet(parcel), irGet(flags))
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for Parcelables in the same module, which accesses the writeToParcel/createFromParcel methods without reflection.
|
||||
class IrEfficientParcelableParcelSerializer(private val irClass: IrClass) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
val creator: IrExpression = irClass.fields.find { it.name == CREATOR_NAME }?.let { creatorField ->
|
||||
irGetField(null, creatorField)
|
||||
} ?: irCall(irClass.creatorGetter!!).apply {
|
||||
dispatchReceiver = irGetObject(irClass.companionObject()!!.symbol)
|
||||
}
|
||||
|
||||
return parcelableCreatorCreateFromParcel(creator, irGet(parcel))
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return parcelableWriteToParcel(irClass, value, irGet(parcel), irGet(flags))
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for Parcelables using reflection.
|
||||
// This needs a reference to the parcelize type itself in order to find the correct class loader to use, see KT-20027.
|
||||
class IrGenericParcelableParcelSerializer(private val parcelizeType: IrType) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return parcelReadParcelable(irGet(parcel), classGetClassLoader(javaClassReference(parcelizeType)))
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return parcelWriteParcelable(irGet(parcel), value, irGet(flags))
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback parcel serializer for unknown types using Parcel.readValue/writeValue.
|
||||
// This needs a reference to the parcelize type itself in order to find the correct class loader to use, see KT-20027.
|
||||
class IrGenericValueParcelSerializer(private val parcelizeType: IrType) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return parcelReadValue(irGet(parcel), classGetClassLoader(javaClassReference(parcelizeType)))
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return parcelWriteValue(irGet(parcel), value)
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer using a custom Parceler object.
|
||||
class IrCustomParcelSerializer(private val parcelerObject: IrClass) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return parcelerCreate(parcelerObject, parcel)
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return parcelerWrite(parcelerObject, parcel, flags, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for array types. This handles both primitive array types (for ShortArray and for primitive arrays using custom element
|
||||
// parcelers) as well as boxed arrays.
|
||||
// TODO: Unsigned array types
|
||||
class IrArrayParcelSerializer(
|
||||
private val arrayType: IrType,
|
||||
private val elementType: IrType,
|
||||
private val elementSerializer: IrParcelSerializer
|
||||
) : IrParcelSerializer {
|
||||
private fun AndroidIrBuilder.newArray(size: IrExpression): IrExpression {
|
||||
val arrayConstructor: IrFunctionSymbol = if (arrayType.isBoxedArray) {
|
||||
androidSymbols.arrayOfNulls
|
||||
} else {
|
||||
arrayType.classOrNull!!.constructors.single { it.owner.valueParameters.size == 1 }
|
||||
}
|
||||
|
||||
return irCall(arrayConstructor, arrayType).apply {
|
||||
if (typeArgumentsCount != 0)
|
||||
putTypeArgument(0, elementType)
|
||||
putValueArgument(0, size)
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return irBlock {
|
||||
val arraySize = irTemporary(parcelReadInt(irGet(parcel)))
|
||||
val arrayTemporary = irTemporary(newArray(irGet(arraySize)))
|
||||
forUntil(irGet(arraySize)) { index ->
|
||||
val setter = arrayType.classOrNull!!.getSimpleFunction("set")!!
|
||||
+irCall(setter).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
putValueArgument(0, irGet(index))
|
||||
putValueArgument(1, readParcelWith(elementSerializer, parcel))
|
||||
}
|
||||
}
|
||||
+irGet(arrayTemporary)
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return irBlock {
|
||||
val arrayTemporary = irTemporary(value)
|
||||
val arraySizeSymbol = arrayType.classOrNull!!.getPropertyGetter("size")!!
|
||||
val arraySize = irTemporary(irCall(arraySizeSymbol).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
})
|
||||
|
||||
+parcelWriteInt(irGet(parcel), irGet(arraySize))
|
||||
|
||||
forUntil(irGet(arraySize)) { index ->
|
||||
val getter = context.irBuiltIns.arrayClass.getSimpleFunction("get")!!
|
||||
val element = irCall(getter, elementType).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
putValueArgument(0, irGet(index))
|
||||
}
|
||||
+writeParcelWith(elementSerializer, parcel, flags, element)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for android SparseArrays. Note that this also needs to handle BooleanSparseArray, in case of a custom element parceler.
|
||||
class IrSparseArrayParcelSerializer(
|
||||
private val sparseArrayClass: IrClass,
|
||||
private val elementType: IrType,
|
||||
private val elementSerializer: IrParcelSerializer
|
||||
) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return irBlock {
|
||||
val remainingSizeTemporary = irTemporaryVar(parcelReadInt(irGet(parcel)))
|
||||
|
||||
val sparseArrayConstructor = sparseArrayClass.constructors.first { irConstructor ->
|
||||
irConstructor.valueParameters.size == 1 && irConstructor.valueParameters.single().type.isInt()
|
||||
}
|
||||
|
||||
val constructorCall = if (sparseArrayClass.typeParameters.isEmpty())
|
||||
irCall(sparseArrayConstructor)
|
||||
else
|
||||
irCallConstructor(sparseArrayConstructor.symbol, listOf(elementType))
|
||||
|
||||
val arrayTemporary = irTemporary(constructorCall.apply {
|
||||
putValueArgument(0, irGet(remainingSizeTemporary))
|
||||
})
|
||||
|
||||
+irWhile().apply {
|
||||
condition = irNotEquals(irGet(remainingSizeTemporary), irInt(0))
|
||||
body = irBlock {
|
||||
val sparseArrayPut = sparseArrayClass.functions.first { function ->
|
||||
function.name.asString() == "put" && function.valueParameters.size == 2
|
||||
}
|
||||
+irCall(sparseArrayPut).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
putValueArgument(0, parcelReadInt(irGet(parcel)))
|
||||
putValueArgument(1, readParcelWith(elementSerializer, parcel))
|
||||
}
|
||||
|
||||
val dec = context.irBuiltIns.intClass.getSimpleFunction("dec")!!
|
||||
+irSetVar(remainingSizeTemporary.symbol, irCall(dec).apply {
|
||||
dispatchReceiver = irGet(remainingSizeTemporary)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+irGet(arrayTemporary)
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return irBlock {
|
||||
val sizeFunction = sparseArrayClass.functions.first { function ->
|
||||
function.name.asString() == "size" && function.valueParameters.isEmpty()
|
||||
}
|
||||
val keyAtFunction = sparseArrayClass.functions.first { function ->
|
||||
function.name.asString() == "keyAt" && function.valueParameters.size == 1
|
||||
}
|
||||
val valueAtFunction = sparseArrayClass.functions.first { function ->
|
||||
function.name.asString() == "valueAt" && function.valueParameters.size == 1
|
||||
}
|
||||
|
||||
val arrayTemporary = irTemporary(value)
|
||||
val sizeTemporary = irTemporary(irCall(sizeFunction).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
})
|
||||
|
||||
+parcelWriteInt(irGet(parcel), irGet(sizeTemporary))
|
||||
|
||||
forUntil(irGet(sizeTemporary)) { index ->
|
||||
+parcelWriteInt(irGet(parcel), irCall(keyAtFunction).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
putValueArgument(0, irGet(index))
|
||||
})
|
||||
|
||||
+writeParcelWith(elementSerializer, parcel, flags, irCall(valueAtFunction.symbol, elementType).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
putValueArgument(0, irGet(index))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for all lists supported by Parcelize. List interfaces use hard-coded default implementations for deserialization.
|
||||
// List maps to ArrayList, Set maps to LinkedHashSet, NavigableSet and SortedSet map to TreeSet.
|
||||
class IrListParcelSerializer(
|
||||
private val irClass: IrClass,
|
||||
private val elementType: IrType,
|
||||
private val elementSerializer: IrParcelSerializer
|
||||
) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
val sizeFunction = irClass.getPropertyGetter("size")!!
|
||||
val iteratorFunction = irClass.getMethodWithoutArguments("iterator")
|
||||
val iteratorClass = iteratorFunction.returnType.erasedUpperBound
|
||||
val iteratorHasNext = iteratorClass.getMethodWithoutArguments("hasNext")
|
||||
val iteratorNext = iteratorClass.getMethodWithoutArguments("next")
|
||||
|
||||
return irBlock {
|
||||
val list = irTemporary(value)
|
||||
+parcelWriteInt(irGet(parcel), irCall(sizeFunction).apply {
|
||||
dispatchReceiver = irGet(list)
|
||||
})
|
||||
val iterator = irTemporary(irCall(iteratorFunction).apply {
|
||||
dispatchReceiver = irGet(list)
|
||||
})
|
||||
+irWhile().apply {
|
||||
condition = irCall(iteratorHasNext).apply { dispatchReceiver = irGet(iterator) }
|
||||
body = writeParcelWith(elementSerializer, parcel, flags, irCall(iteratorNext.symbol, elementType).apply {
|
||||
dispatchReceiver = irGet(iterator)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun listSymbols(symbols: AndroidSymbols): Pair<IrConstructorSymbol, IrSimpleFunctionSymbol> {
|
||||
// If the IrClass refers to a concrete type, try to find a constructor with capacity or fall back
|
||||
// the the default constructor if none exist.
|
||||
if (!irClass.isJvmInterface) {
|
||||
val constructor = irClass.constructors.find { constructor ->
|
||||
constructor.valueParameters.size == 1 && constructor.valueParameters.single().type.isInt()
|
||||
} ?: irClass.constructors.first { constructor -> constructor.valueParameters.isEmpty() }
|
||||
|
||||
val add = irClass.functions.first { function ->
|
||||
function.name.asString() == "add" && function.valueParameters.size == 1
|
||||
}
|
||||
|
||||
return constructor.symbol to add.symbol
|
||||
}
|
||||
|
||||
return when (irClass.fqNameWhenAvailable?.asString()) {
|
||||
"kotlin.collections.MutableList", "kotlin.collections.List", "java.util.List" ->
|
||||
symbols.arrayListConstructor to symbols.arrayListAdd
|
||||
"kotlin.collections.MutableSet", "kotlin.collections.Set", "java.util.Set" ->
|
||||
symbols.linkedHashSetConstructor to symbols.linkedHashSetAdd
|
||||
"java.util.NavigableSet", "java.util.SortedSet" ->
|
||||
symbols.treeSetConstructor to symbols.treeSetAdd
|
||||
else -> error("Unknown list interface type: ${irClass.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return irBlock {
|
||||
val (constructorSymbol, addSymbol) = listSymbols(androidSymbols)
|
||||
val sizeTemporary = irTemporary(parcelReadInt(irGet(parcel)))
|
||||
val list = irTemporary(irCall(constructorSymbol).apply {
|
||||
if (constructorSymbol.owner.valueParameters.isNotEmpty())
|
||||
putValueArgument(0, irGet(sizeTemporary))
|
||||
})
|
||||
forUntil(irGet(sizeTemporary)) {
|
||||
+irCall(addSymbol).apply {
|
||||
dispatchReceiver = irGet(list)
|
||||
putValueArgument(0, readParcelWith(elementSerializer, parcel))
|
||||
}
|
||||
}
|
||||
+irGet(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for all maps supported by Parcelize. Map interfaces use hard-coded default implementations for deserialization.
|
||||
// Map uses LinkedHashMap, while NavigableMap and SortedMap use to TreeMap.
|
||||
class IrMapParcelSerializer(
|
||||
private val irClass: IrClass,
|
||||
private val keyType: IrType,
|
||||
private val valueType: IrType,
|
||||
private val keySerializer: IrParcelSerializer,
|
||||
private val valueSerializer: IrParcelSerializer
|
||||
) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
val sizeFunction = irClass.getPropertyGetter("size")!!
|
||||
val entriesFunction = irClass.getPropertyGetter("entries")!!
|
||||
val entrySetClass = entriesFunction.owner.returnType.erasedUpperBound
|
||||
val iteratorFunction = entrySetClass.getMethodWithoutArguments("iterator")
|
||||
val iteratorClass = iteratorFunction.returnType.erasedUpperBound
|
||||
val iteratorHasNext = iteratorClass.getMethodWithoutArguments("hasNext")
|
||||
val iteratorNext = iteratorClass.getMethodWithoutArguments("next")
|
||||
val elementClass =
|
||||
(entriesFunction.owner.returnType as IrSimpleType).arguments.single().upperBound(context.irBuiltIns).erasedUpperBound
|
||||
val elementKey = elementClass.getPropertyGetter("key")!!
|
||||
val elementValue = elementClass.getPropertyGetter("value")!!
|
||||
|
||||
return irBlock {
|
||||
val list = irTemporary(value)
|
||||
+parcelWriteInt(irGet(parcel), irCall(sizeFunction).apply {
|
||||
dispatchReceiver = irGet(list)
|
||||
})
|
||||
val iterator = irTemporary(irCall(iteratorFunction).apply {
|
||||
dispatchReceiver = irCall(entriesFunction).apply {
|
||||
dispatchReceiver = irGet(list)
|
||||
}
|
||||
})
|
||||
+irWhile().apply {
|
||||
condition = irCall(iteratorHasNext).apply { dispatchReceiver = irGet(iterator) }
|
||||
body = irBlock {
|
||||
val element = irTemporary(irCall(iteratorNext).apply {
|
||||
dispatchReceiver = irGet(iterator)
|
||||
})
|
||||
+writeParcelWith(keySerializer, parcel, flags, irCall(elementKey, keyType).apply {
|
||||
dispatchReceiver = irGet(element)
|
||||
})
|
||||
+writeParcelWith(valueSerializer, parcel, flags, irCall(elementValue, valueType).apply {
|
||||
dispatchReceiver = irGet(element)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapSymbols(symbols: AndroidSymbols): Pair<IrConstructorSymbol, IrSimpleFunctionSymbol> {
|
||||
// If the IrClass refers to a concrete type, try to find a constructor with capacity or fall back
|
||||
// the the default constructor if none exist.
|
||||
if (!irClass.isJvmInterface) {
|
||||
val constructor = irClass.constructors.find { constructor ->
|
||||
constructor.valueParameters.size == 1 && constructor.valueParameters.single().type.isInt()
|
||||
} ?: irClass.constructors.find { constructor ->
|
||||
constructor.valueParameters.isEmpty()
|
||||
}!!
|
||||
|
||||
val put = irClass.functions.first { function ->
|
||||
function.name.asString() == "put" && function.valueParameters.size == 2
|
||||
}
|
||||
|
||||
return constructor.symbol to put.symbol
|
||||
}
|
||||
|
||||
return when (irClass.fqNameWhenAvailable?.asString()) {
|
||||
"kotlin.collections.MutableMap", "kotlin.collections.Map", "java.util.Map" ->
|
||||
symbols.linkedHashMapConstructor to symbols.linkedHashMapPut
|
||||
"java.util.SortedMap", "java.util.NavigableMap" ->
|
||||
symbols.treeMapConstructor to symbols.treeMapPut
|
||||
else -> error("Unknown map interface type: ${irClass.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return irBlock {
|
||||
val (constructorSymbol, putSymbol) = mapSymbols(androidSymbols)
|
||||
val sizeTemporary = irTemporary(parcelReadInt(irGet(parcel)))
|
||||
val map = irTemporary(irCall(constructorSymbol).apply {
|
||||
if (constructorSymbol.owner.valueParameters.isNotEmpty())
|
||||
putValueArgument(0, irGet(sizeTemporary))
|
||||
})
|
||||
forUntil(irGet(sizeTemporary)) {
|
||||
+irCall(putSymbol).apply {
|
||||
dispatchReceiver = irGet(map)
|
||||
putValueArgument(0, readParcelWith(keySerializer, parcel))
|
||||
putValueArgument(1, readParcelWith(valueSerializer, parcel))
|
||||
}
|
||||
}
|
||||
+irGet(map)
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize.ir
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.getClass
|
||||
import org.jetbrains.kotlin.ir.types.typeOrNull
|
||||
import org.jetbrains.kotlin.ir.util.constructedClass
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.getAnnotation
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeAnnotationChecker
|
||||
|
||||
// Keep track of all custom parcelers which are currently in scope.
|
||||
// Note that custom parcelers are resolved in *reverse* lexical order.
|
||||
class IrParcelerScope(private val parent: IrParcelerScope? = null) {
|
||||
private val typeParcelers = mutableMapOf<IrType, IrClass>()
|
||||
|
||||
fun add(type: IrType, parceler: IrClass) {
|
||||
typeParcelers.putIfAbsent(type, parceler)
|
||||
}
|
||||
|
||||
fun get(type: IrType): IrClass? =
|
||||
parent?.get(type) ?: typeParcelers[type]
|
||||
}
|
||||
|
||||
fun IrParcelerScope?.getCustomSerializer(irType: IrType): IrClass? {
|
||||
return irType.getAnnotation(ParcelizeAnnotationChecker.WRITE_WITH_FQNAME)?.let { writeWith ->
|
||||
(writeWith.type as IrSimpleType).arguments.single().typeOrNull!!.getClass()!!
|
||||
} ?: this?.get(irType)
|
||||
}
|
||||
|
||||
fun IrParcelerScope?.hasCustomSerializer(irType: IrType): Boolean {
|
||||
return getCustomSerializer(irType) != null
|
||||
}
|
||||
|
||||
fun IrAnnotationContainer.getParcelerScope(parent: IrParcelerScope? = null): IrParcelerScope? {
|
||||
val typeParcelerAnnotations = annotations.filterTo(mutableListOf()) {
|
||||
it.symbol.owner.constructedClass.fqNameWhenAvailable == ParcelizeAnnotationChecker.TYPE_PARCELER_FQNAME
|
||||
}
|
||||
|
||||
if (typeParcelerAnnotations.isEmpty())
|
||||
return parent
|
||||
|
||||
val scope = IrParcelerScope(parent)
|
||||
|
||||
for (annotation in typeParcelerAnnotations) {
|
||||
val (mappedType, parcelerType) = (annotation.type as IrSimpleType).arguments.map { it.typeOrNull!! }
|
||||
scope.add(mappedType, parcelerType.getClass()!!)
|
||||
}
|
||||
|
||||
return scope
|
||||
}
|
||||
+322
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.parcelize.ANDROID_PARCELABLE_CLASS_FQNAME
|
||||
import org.jetbrains.kotlin.parcelize.PARCELER_FQNAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent
|
||||
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
class ParcelizeIrTransformer(private val context: IrPluginContext, private val androidSymbols: AndroidSymbols) :
|
||||
ParcelizeExtensionBase, IrElementVisitorVoid {
|
||||
private val serializerFactory = IrParcelSerializerFactory(androidSymbols)
|
||||
|
||||
private val deferredOperations = mutableListOf<() -> Unit>()
|
||||
private fun defer(block: () -> Unit) = deferredOperations.add(block)
|
||||
|
||||
private fun IrPluginContext.createIrBuilder(symbol: IrSymbol) =
|
||||
DeclarationIrBuilder(this, symbol, symbol.owner.startOffset, symbol.owner.endOffset)
|
||||
|
||||
private val symbolMap = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunctionSymbol>()
|
||||
|
||||
private val irFactory: IrFactory = IrFactoryImpl
|
||||
|
||||
fun transform(moduleFragment: IrModuleFragment) {
|
||||
moduleFragment.accept(this, null)
|
||||
deferredOperations.forEach { it() }
|
||||
|
||||
// Remap broken stubs, which psi2ir generates for the synthetic descriptors coming from the ParcelizeResolveExtension.
|
||||
moduleFragment.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
val remappedSymbol = symbolMap[expression.symbol]
|
||||
?: return super.visitCall(expression)
|
||||
return IrCallImpl(
|
||||
expression.startOffset, expression.endOffset, expression.type, remappedSymbol,
|
||||
expression.typeArgumentsCount, expression.valueArgumentsCount, expression.origin,
|
||||
expression.superQualifierSymbol
|
||||
).apply {
|
||||
copyTypeAndValueArgumentsFrom(expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||
val remappedSymbol = symbolMap[expression.symbol]
|
||||
val remappedReflectionTarget = expression.reflectionTarget?.let { symbolMap[it] }
|
||||
if (remappedSymbol == null && remappedReflectionTarget == null)
|
||||
return super.visitFunctionReference(expression)
|
||||
|
||||
return IrFunctionReferenceImpl(
|
||||
expression.startOffset, expression.endOffset, expression.type, remappedSymbol ?: expression.symbol,
|
||||
expression.typeArgumentsCount, expression.valueArgumentsCount, remappedReflectionTarget,
|
||||
expression.origin
|
||||
).apply {
|
||||
copyTypeAndValueArgumentsFrom(expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement {
|
||||
// Remap overridden symbols, otherwise the code might break in BridgeLowering
|
||||
declaration.overriddenSymbols = declaration.overriddenSymbols.map { symbol ->
|
||||
symbolMap[symbol] ?: symbol
|
||||
}
|
||||
return super.visitSimpleFunction(declaration)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement) = element.acceptChildren(this, null)
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
declaration.acceptChildren(this, null)
|
||||
if (!declaration.isParcelize)
|
||||
return
|
||||
|
||||
val parcelableProperties = declaration.parcelableProperties
|
||||
|
||||
// If the companion extends Parceler, it can override parts of the generated implementation.
|
||||
val parcelerObject = declaration.companionObject()?.takeIf {
|
||||
it.isSubclassOfFqName(PARCELER_FQNAME.asString())
|
||||
}
|
||||
|
||||
if (declaration.descriptor.hasSyntheticDescribeContents()) {
|
||||
val describeContents = declaration.addOverride(
|
||||
ANDROID_PARCELABLE_CLASS_FQNAME,
|
||||
"describeContents",
|
||||
context.irBuiltIns.intType,
|
||||
modality = Modality.OPEN
|
||||
).apply {
|
||||
val flags = if (parcelableProperties.any { it.field.type.containsFileDescriptors }) 1 else 0
|
||||
body = context.createIrBuilder(symbol).run {
|
||||
irExprBody(irInt(flags))
|
||||
}
|
||||
|
||||
metadata = DescriptorMetadataSource.Function(
|
||||
declaration.descriptor.findFunction(ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS)!!
|
||||
)
|
||||
}
|
||||
|
||||
declaration.functions.find {
|
||||
it.descriptor.safeAs<ParcelizeSyntheticComponent>()?.componentKind == ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS
|
||||
}?.let { stub ->
|
||||
symbolMap[stub.symbol] = describeContents.symbol
|
||||
declaration.declarations.remove(stub)
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration.descriptor.hasSyntheticWriteToParcel()) {
|
||||
val writeToParcel = declaration.addOverride(
|
||||
ANDROID_PARCELABLE_CLASS_FQNAME,
|
||||
"writeToParcel",
|
||||
context.irBuiltIns.unitType,
|
||||
modality = Modality.OPEN
|
||||
).apply {
|
||||
val receiverParameter = dispatchReceiverParameter!!
|
||||
val parcelParameter = addValueParameter("out", androidSymbols.androidOsParcel.defaultType)
|
||||
val flagsParameter = addValueParameter("flags", context.irBuiltIns.intType)
|
||||
|
||||
// We need to defer the construction of the writer, since it may refer to the [writeToParcel] methods in other
|
||||
// @Parcelize classes in the current module, which might not be constructed yet at this point.
|
||||
defer {
|
||||
body = androidSymbols.createBuilder(symbol).run {
|
||||
irBlockBody {
|
||||
when {
|
||||
parcelerObject != null ->
|
||||
+parcelerWrite(parcelerObject, parcelParameter, flagsParameter, irGet(receiverParameter))
|
||||
|
||||
parcelableProperties.isNotEmpty() ->
|
||||
for (property in parcelableProperties) {
|
||||
+writeParcelWith(
|
||||
property.parceler,
|
||||
parcelParameter,
|
||||
flagsParameter,
|
||||
irGetField(irGet(receiverParameter), property.field)
|
||||
)
|
||||
}
|
||||
|
||||
else ->
|
||||
+writeParcelWith(
|
||||
declaration.classParceler,
|
||||
parcelParameter,
|
||||
flagsParameter,
|
||||
irGet(receiverParameter)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
metadata = DescriptorMetadataSource.Function(
|
||||
declaration.descriptor.findFunction(ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL)!!
|
||||
)
|
||||
}
|
||||
|
||||
declaration.functions.find {
|
||||
it.descriptor.safeAs<ParcelizeSyntheticComponent>()?.componentKind == ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL
|
||||
}?.let { stub ->
|
||||
symbolMap[stub.symbol] = writeToParcel.symbol
|
||||
declaration.declarations.remove(stub)
|
||||
}
|
||||
}
|
||||
|
||||
val creatorType = androidSymbols.androidOsParcelableCreator.typeWith(declaration.defaultType)
|
||||
|
||||
if (!declaration.descriptor.hasCreatorField()) {
|
||||
declaration.addField {
|
||||
name = ParcelizeExtensionBase.CREATOR_NAME
|
||||
type = creatorType
|
||||
isStatic = true
|
||||
isFinal = true
|
||||
}.apply {
|
||||
val irField = this
|
||||
val creatorClass = irFactory.buildClass {
|
||||
name = Name.identifier("Creator")
|
||||
visibility = DescriptorVisibilities.LOCAL
|
||||
}.apply {
|
||||
parent = irField
|
||||
superTypes = listOf(creatorType)
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
|
||||
addConstructor {
|
||||
isPrimary = true
|
||||
}.apply {
|
||||
body = context.createIrBuilder(symbol).irBlockBody {
|
||||
+irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single())
|
||||
}
|
||||
}
|
||||
|
||||
val arrayType = context.irBuiltIns.arrayClass.typeWith(declaration.defaultType.makeNullable())
|
||||
addFunction("newArray", arrayType).apply {
|
||||
overriddenSymbols = listOf(androidSymbols.androidOsParcelableCreator.getSimpleFunction(name.asString())!!)
|
||||
val sizeParameter = addValueParameter("size", context.irBuiltIns.intType)
|
||||
body = context.createIrBuilder(symbol).run {
|
||||
irExprBody(
|
||||
parcelerNewArray(parcelerObject, sizeParameter)
|
||||
?: irCall(androidSymbols.arrayOfNulls, arrayType).apply {
|
||||
putTypeArgument(0, arrayType)
|
||||
putValueArgument(0, irGet(sizeParameter))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
addFunction("createFromParcel", declaration.defaultType).apply {
|
||||
overriddenSymbols = listOf(androidSymbols.androidOsParcelableCreator.getSimpleFunction(name.asString())!!)
|
||||
val parcelParameter = addValueParameter("parcel", androidSymbols.androidOsParcel.defaultType)
|
||||
|
||||
// We need to defer the construction of the create method, since it may refer to the [Parcelable.Creator]
|
||||
// instances in other @Parcelize classes in the current module, which may not exist yet.
|
||||
defer {
|
||||
body = androidSymbols.createBuilder(symbol).run {
|
||||
irExprBody(
|
||||
when {
|
||||
parcelerObject != null ->
|
||||
parcelerCreate(parcelerObject, parcelParameter)
|
||||
|
||||
parcelableProperties.isNotEmpty() ->
|
||||
irCall(declaration.primaryConstructor!!).apply {
|
||||
for ((index, property) in parcelableProperties.withIndex()) {
|
||||
putValueArgument(index, readParcelWith(property.parceler, parcelParameter))
|
||||
}
|
||||
}
|
||||
|
||||
else ->
|
||||
readParcelWith(declaration.classParceler, parcelParameter)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initializer = context.createIrBuilder(symbol).run {
|
||||
irExprBody(irBlock {
|
||||
+creatorClass
|
||||
+irCall(creatorClass.primaryConstructor!!)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrClass.addOverride(
|
||||
baseFqName: FqName,
|
||||
name: String,
|
||||
returnType: IrType,
|
||||
modality: Modality = Modality.FINAL
|
||||
): IrSimpleFunction = addFunction(name, returnType, modality).apply {
|
||||
overriddenSymbols = superTypes.mapNotNull { superType ->
|
||||
superType.classOrNull?.owner?.takeIf { superClass -> superClass.isSubclassOfFqName(baseFqName.asString()) }
|
||||
}.flatMap { superClass ->
|
||||
superClass.functions.filter { function ->
|
||||
function.name.asString() == name && function.overridesFunctionIn(baseFqName)
|
||||
}.map { it.symbol }.toList()
|
||||
}
|
||||
}
|
||||
|
||||
private class ParcelableProperty(val field: IrField, parcelerThunk: () -> IrParcelSerializer) {
|
||||
val parceler by lazy(parcelerThunk)
|
||||
}
|
||||
|
||||
private val IrClass.classParceler: IrParcelSerializer
|
||||
get() = if (kind == ClassKind.CLASS) {
|
||||
IrNoParameterClassParcelSerializer(this)
|
||||
} else {
|
||||
serializerFactory.get(defaultType, parcelizeType = defaultType, strict = true, toplevel = true, scope = getParcelerScope())
|
||||
}
|
||||
|
||||
private val IrClass.parcelableProperties: List<ParcelableProperty>
|
||||
get() {
|
||||
if (kind != ClassKind.CLASS) return emptyList()
|
||||
|
||||
val constructor = primaryConstructor ?: return emptyList()
|
||||
val topLevelScope = getParcelerScope()
|
||||
|
||||
return constructor.valueParameters.map { parameter ->
|
||||
val property = properties.first { it.name == parameter.name }
|
||||
val localScope = property.getParcelerScope(topLevelScope)
|
||||
ParcelableProperty(property.backingField!!) {
|
||||
serializerFactory.get(parameter.type, parcelizeType = defaultType, scope = localScope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// *Heuristic* to determine if a Parcelable contains file descriptors.
|
||||
private val IrType.containsFileDescriptors: Boolean
|
||||
get() = erasedUpperBound.fqNameWhenAvailable == ParcelizeExtensionBase.FILE_DESCRIPTOR_FQNAME ||
|
||||
(this as? IrSimpleType)?.arguments?.any { argument ->
|
||||
argument.typeOrNull?.containsFileDescriptors == true
|
||||
} == true
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.parcelize.ANDROID_PARCELABLE_CLASS_FQNAME
|
||||
import org.jetbrains.kotlin.parcelize.ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME
|
||||
import org.jetbrains.kotlin.parcelize.PARCELER_FQNAME
|
||||
import org.jetbrains.kotlin.parcelize.PARCELIZE_CLASS_FQNAME
|
||||
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase
|
||||
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase.Companion.CREATOR_NAME
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
// true if the class should be processed by the parcelize plugin
|
||||
val IrClass.isParcelize: Boolean
|
||||
get() = kind in ParcelizeExtensionBase.ALLOWED_CLASS_KINDS && hasAnnotation(PARCELIZE_CLASS_FQNAME)
|
||||
|
||||
// Finds the getter for a pre-existing CREATOR field on the class companion, which is used for manual Parcelable implementations in Kotlin.
|
||||
val IrClass.creatorGetter: IrSimpleFunctionSymbol?
|
||||
get() = companionObject()?.getPropertyGetter(CREATOR_NAME.asString())?.takeIf {
|
||||
it.owner.correspondingPropertySymbol?.owner?.backingField?.hasAnnotation(FqName("kotlin.jvm.JvmField")) == true
|
||||
}
|
||||
|
||||
// true if the class has a static CREATOR field
|
||||
val IrClass.hasCreatorField: Boolean
|
||||
get() = fields.any { field -> field.name == CREATOR_NAME } || creatorGetter != null
|
||||
|
||||
// object P : Parceler<T> { fun T.write(parcel: Parcel, flags: Int) ...}
|
||||
fun IrBuilderWithScope.parcelerWrite(
|
||||
parceler: IrClass, parcel: IrValueDeclaration,
|
||||
flags: IrValueDeclaration, value: IrExpression
|
||||
): IrCall {
|
||||
return irCall(parceler.parcelerSymbolByName("write")!!).apply {
|
||||
dispatchReceiver = irGetObject(parceler.symbol)
|
||||
extensionReceiver = value
|
||||
putValueArgument(0, irGet(parcel))
|
||||
putValueArgument(1, irGet(flags))
|
||||
}
|
||||
}
|
||||
|
||||
// object P : Parceler<T> { fun create(parcel: Parcel): T }
|
||||
fun IrBuilderWithScope.parcelerCreate(parceler: IrClass, parcel: IrValueDeclaration): IrExpression {
|
||||
return irCall(parceler.parcelerSymbolByName("create")!!).apply {
|
||||
dispatchReceiver = irGetObject(parceler.symbol)
|
||||
putValueArgument(0, irGet(parcel))
|
||||
}
|
||||
}
|
||||
|
||||
// object P: Parceler<T> { fun newArray(size: Int): Array<T> }
|
||||
fun IrBuilderWithScope.parcelerNewArray(parceler: IrClass?, size: IrValueDeclaration): IrExpression? {
|
||||
return parceler?.parcelerSymbolByName("newArray")?.let { newArraySymbol ->
|
||||
irCall(newArraySymbol).apply {
|
||||
dispatchReceiver = irGetObject(parceler.symbol)
|
||||
putValueArgument(0, irGet(size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// class Parcelable { fun writeToParcel(parcel: Parcel, flags: Int) ...}
|
||||
fun IrBuilderWithScope.parcelableWriteToParcel(
|
||||
parcelableClass: IrClass,
|
||||
parcelable: IrExpression,
|
||||
parcel: IrExpression,
|
||||
flags: IrExpression
|
||||
): IrExpression {
|
||||
val writeToParcel = parcelableClass.functions.first { function ->
|
||||
function.name.asString() == "writeToParcel" && function.overridesFunctionIn(ANDROID_PARCELABLE_CLASS_FQNAME)
|
||||
}
|
||||
|
||||
return irCall(writeToParcel).apply {
|
||||
dispatchReceiver = parcelable
|
||||
putValueArgument(0, parcel)
|
||||
putValueArgument(1, flags)
|
||||
}
|
||||
}
|
||||
|
||||
// class C : Parcelable.Creator<T> { fun createFromParcel(parcel: Parcel): T ...}
|
||||
fun IrBuilderWithScope.parcelableCreatorCreateFromParcel(creator: IrExpression, parcel: IrExpression): IrExpression {
|
||||
val createFromParcel = creator.type.getClass()!!.functions.first { function ->
|
||||
function.name.asString() == "createFromParcel" && function.overridesFunctionIn(ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME)
|
||||
}
|
||||
|
||||
return irCall(createFromParcel).apply {
|
||||
dispatchReceiver = creator
|
||||
putValueArgument(0, parcel)
|
||||
}
|
||||
}
|
||||
|
||||
// Find a named function declaration which overrides the corresponding function in [Parceler].
|
||||
// This is more reliable than trying to match the functions signature ourselves, since the frontend
|
||||
// has already done the work.
|
||||
private fun IrClass.parcelerSymbolByName(name: String): IrSimpleFunctionSymbol? {
|
||||
return functions.firstOrNull { function ->
|
||||
!function.isFakeOverride && function.name.asString() == name && function.overridesFunctionIn(PARCELER_FQNAME)
|
||||
}?.symbol
|
||||
}
|
||||
|
||||
fun IrSimpleFunction.overridesFunctionIn(fqName: FqName): Boolean {
|
||||
return parentClassOrNull?.fqNameWhenAvailable == fqName || allOverridden().any { it.parentClassOrNull?.fqNameWhenAvailable == fqName }
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.kClassReference(classType: IrType): IrClassReferenceImpl {
|
||||
return IrClassReferenceImpl(
|
||||
startOffset, endOffset, context.irBuiltIns.kClassClass.starProjectedType, context.irBuiltIns.kClassClass, classType
|
||||
)
|
||||
}
|
||||
|
||||
private fun AndroidIrBuilder.kClassToJavaClass(kClassReference: IrExpression): IrCall {
|
||||
return irGet(androidSymbols.javaLangClass.starProjectedType, null, androidSymbols.kotlinKClassJava.owner.getter!!.symbol).apply {
|
||||
extensionReceiver = kClassReference
|
||||
}
|
||||
}
|
||||
|
||||
// Produce a static reference to the java class of the given type.
|
||||
fun AndroidIrBuilder.javaClassReference(classType: IrType): IrCall = kClassToJavaClass(kClassReference(classType))
|
||||
|
||||
fun IrClass.isSubclassOfFqName(fqName: String): Boolean {
|
||||
return fqNameWhenAvailable?.asString() == fqName || superTypes.any { it.erasedUpperBound.isSubclassOfFqName(fqName) }
|
||||
}
|
||||
|
||||
inline fun IrBlockBuilder.forUntil(upperBound: IrExpression, loopBody: IrBlockBuilder.(IrValueDeclaration) -> Unit) {
|
||||
val indexTemporary = irTemporaryVar(irInt(0))
|
||||
+irWhile().apply {
|
||||
condition = irNotEquals(irGet(indexTemporary), upperBound)
|
||||
body = irBlock {
|
||||
loopBody(indexTemporary)
|
||||
val inc = context.irBuiltIns.intClass.getSimpleFunction("inc")!!
|
||||
+irSetVar(indexTemporary.symbol, irCall(inc).apply {
|
||||
dispatchReceiver = irGet(indexTemporary)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun IrTypeArgument.upperBound(builtIns: IrBuiltIns): IrType =
|
||||
when (this) {
|
||||
is IrStarProjection -> builtIns.anyNType
|
||||
is IrTypeProjection -> {
|
||||
if (variance == Variance.OUT_VARIANCE || variance == Variance.INVARIANT)
|
||||
type
|
||||
else
|
||||
builtIns.anyNType
|
||||
}
|
||||
else -> error("Unknown type argument: ${render()}")
|
||||
}
|
||||
|
||||
private fun IrClass.getSimpleFunction(name: String): IrSimpleFunctionSymbol? =
|
||||
findDeclaration<IrSimpleFunction> { it.name.asString() == name }?.symbol
|
||||
|
||||
// This is a version of getPropertyGetter which does not throw when applied to broken lazy classes, such as java.util.HashMap,
|
||||
// which contains two "size" properties with different visibilities.
|
||||
fun IrClass.getPropertyGetter(name: String): IrSimpleFunctionSymbol? =
|
||||
declarations.filterIsInstance<IrProperty>().firstOrNull { it.name.asString() == name && it.getter != null }?.getter?.symbol
|
||||
?: getSimpleFunction("<get-$name>")
|
||||
|
||||
fun IrClass.getMethodWithoutArguments(name: String): IrSimpleFunction =
|
||||
functions.first { function ->
|
||||
function.name.asString() == name && function.dispatchReceiverParameter != null
|
||||
&& function.extensionReceiverParameter == null && function.valueParameters.isEmpty()
|
||||
}
|
||||
+449
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* 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.parcelize.serializers
|
||||
|
||||
import kotlinx.parcelize.WriteWith
|
||||
import org.jetbrains.kotlin.codegen.FrameMap
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.parcelize.isParcelize
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||
import org.jetbrains.kotlin.synthetic.isVisibleOutside
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.isError
|
||||
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
|
||||
|
||||
val RAW_VALUE_ANNOTATION_FQNAME = FqName("kotlinx.parcelize.RawValue")
|
||||
|
||||
internal typealias TypeParcelerMapping = Pair<KotlinType, KotlinType>
|
||||
|
||||
interface ParcelSerializer {
|
||||
val asmType: Type
|
||||
|
||||
fun writeValue(v: InstructionAdapter)
|
||||
fun readValue(v: InstructionAdapter)
|
||||
|
||||
data class ParcelSerializerContext(
|
||||
val typeMapper: KotlinTypeMapper,
|
||||
val containerClassType: Type,
|
||||
val typeParcelers: List<TypeParcelerMapping>,
|
||||
val frameMap: FrameMap
|
||||
) {
|
||||
fun findParcelerClass(type: KotlinType): KotlinType? {
|
||||
return typeParcelers.firstOrNull { it.first == type }?.second
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val WRITE_WITH_FQNAME = FqName(WriteWith::class.java.name)
|
||||
|
||||
private fun KotlinTypeMapper.mapTypeSafe(type: KotlinType, forceBoxed: Boolean) = when {
|
||||
type.isError -> Type.getObjectType("java/lang/Object")
|
||||
else -> mapType(type, null, if (forceBoxed) TypeMappingMode.GENERIC_ARGUMENT else TypeMappingMode.DEFAULT)
|
||||
}
|
||||
|
||||
fun get(
|
||||
type: KotlinType,
|
||||
asmType: Type,
|
||||
context: ParcelSerializerContext,
|
||||
forceBoxed: Boolean = false,
|
||||
strict: Boolean = false
|
||||
): ParcelSerializer {
|
||||
val typeMapper = context.typeMapper
|
||||
|
||||
val className = asmType.className
|
||||
fun strict() = strict && !type.annotations.hasAnnotation(RAW_VALUE_ANNOTATION_FQNAME)
|
||||
|
||||
fun findCustomParcelerType(type: KotlinType): KotlinType? {
|
||||
type.annotations.findAnnotation(WRITE_WITH_FQNAME)?.let { writeWith ->
|
||||
val parceler = writeWith.type.arguments.singleOrNull()?.type
|
||||
if (parceler != null && !parceler.isError) {
|
||||
return parceler
|
||||
}
|
||||
}
|
||||
|
||||
return context.findParcelerClass(type)?.takeIf { !it.isError }
|
||||
}
|
||||
|
||||
findCustomParcelerType(type)?.let { return TypeParcelerParcelSerializer(asmType, it, context.typeMapper) }
|
||||
|
||||
return when {
|
||||
asmType.descriptor == "[I"
|
||||
|| asmType.descriptor == "[Z"
|
||||
|| asmType.descriptor == "[B"
|
||||
|| asmType.descriptor == "[C"
|
||||
|| asmType.descriptor == "[S"
|
||||
|| asmType.descriptor == "[D"
|
||||
|| asmType.descriptor == "[F"
|
||||
|| asmType.descriptor == "[J"
|
||||
-> {
|
||||
val customElementParcelerType = findCustomParcelerType(type.builtIns.getArrayElementType(type))
|
||||
if (customElementParcelerType != null) {
|
||||
val elementType = asmType.elementType
|
||||
val elementParceler = TypeParcelerParcelSerializer(elementType, customElementParcelerType, context.typeMapper)
|
||||
ArrayParcelSerializer(asmType, elementParceler)
|
||||
} else {
|
||||
PrimitiveArrayParcelSerializer(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
asmType.descriptor == "[Landroid/os/IBinder;" -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeBinderArray"), Method("createBinderArray")
|
||||
)
|
||||
}
|
||||
|
||||
asmType.descriptor == "[Ljava/lang/String;" -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeStringArray"), Method("createStringArray")
|
||||
)
|
||||
}
|
||||
|
||||
asmType.sort == Type.ARRAY -> {
|
||||
val elementType = type.builtIns.getArrayElementType(type)
|
||||
val elementSerializer =
|
||||
get(elementType, typeMapper.mapTypeSafe(elementType, forceBoxed = true), context, strict = strict())
|
||||
|
||||
wrapToNullAwareIfNeeded(type, ArrayParcelSerializer(asmType, elementSerializer))
|
||||
}
|
||||
|
||||
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 == SortedSet::class.java.canonicalName
|
||||
|| className == NavigableSet::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 elementAsmType = typeMapper.mapTypeSafe(elementType, forceBoxed = true)
|
||||
|
||||
if (className == List::class.java.canonicalName) {
|
||||
// Don't care if the element type is nullable cause both writeStrongBinder() and writeString() support null values
|
||||
if (elementAsmType.descriptor == "Landroid/os/IBinder;") {
|
||||
return NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeBinderList"), Method("createBinderArrayList", "()Ljava/util/ArrayList;")
|
||||
)
|
||||
} else if (elementAsmType.descriptor == "Ljava/lang/String;") {
|
||||
return NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeStringList"), Method("createStringArrayList", "()Ljava/util/ArrayList;")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val elementSerializer = get(elementType, elementAsmType, context, forceBoxed = true, strict = strict())
|
||||
wrapToNullAwareIfNeeded(type, ListSetParcelSerializer(asmType, elementSerializer, context.frameMap))
|
||||
}
|
||||
|
||||
className == Map::class.java.canonicalName
|
||||
|| className == SortedMap::class.java.canonicalName
|
||||
|| className == NavigableMap::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.mapTypeSafe(keyType.type, forceBoxed = true), context, forceBoxed = true, strict = strict()
|
||||
)
|
||||
val valueSerializer = get(
|
||||
valueType.type,
|
||||
typeMapper.mapTypeSafe(valueType.type, forceBoxed = true),
|
||||
context,
|
||||
forceBoxed = true,
|
||||
strict = strict()
|
||||
)
|
||||
wrapToNullAwareIfNeeded(type, MapParcelSerializer(asmType, keySerializer, valueSerializer, context.frameMap))
|
||||
}
|
||||
|
||||
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")
|
||||
)
|
||||
}
|
||||
|
||||
type.isIBinder() -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeStrongBinder", "(Landroid/os/IBinder;)V"),
|
||||
Method("readStrongBinder", "()Landroid/os/IBinder;")
|
||||
)
|
||||
}
|
||||
|
||||
type.isIInterface() -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeStrongInterface", "(Landroid/os/IInterface;)V"),
|
||||
Method("readStrongInterface", "()Landroid/os/IInterface;")
|
||||
)
|
||||
}
|
||||
|
||||
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), context.frameMap
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
asmType.isSparseLongArray() -> {
|
||||
wrapToNullAwareIfNeeded(
|
||||
type, SparseArrayParcelSerializer(
|
||||
asmType, PrimitiveTypeParcelSerializer.getInstance(Type.LONG_TYPE), context.frameMap
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
asmType.isSparseArray() -> {
|
||||
val elementType = type.arguments.single().type
|
||||
val elementSerializer = get(
|
||||
elementType, typeMapper.mapTypeSafe(elementType, forceBoxed = true), context, forceBoxed = true, strict = strict()
|
||||
)
|
||||
wrapToNullAwareIfNeeded(type, SparseArrayParcelSerializer(asmType, elementSerializer, context.frameMap))
|
||||
}
|
||||
|
||||
type.isCharSequence() -> {
|
||||
CharSequenceParcelSerializer(asmType)
|
||||
}
|
||||
|
||||
type.isException() -> {
|
||||
wrapToNullAwareIfNeeded(
|
||||
type, NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeException"),
|
||||
Method("readException")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
asmType.isFileDescriptor() -> {
|
||||
wrapToNullAwareIfNeeded(
|
||||
type, NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeRawFileDescriptor"),
|
||||
Method("readRawFileDescriptor")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Write at least a nullability byte.
|
||||
// We don't want parcel to be empty in case if all constructor parameters are objects
|
||||
type.isNamedObject() -> {
|
||||
NullAwareParcelSerializerWrapper(ObjectParcelSerializer(asmType, type, typeMapper))
|
||||
}
|
||||
|
||||
type.isEnum() -> {
|
||||
wrapToNullAwareIfNeeded(type, EnumParcelSerializer(asmType))
|
||||
}
|
||||
|
||||
type.isParcelable() -> {
|
||||
val clazz = type.constructor.declarationDescriptor as? ClassDescriptor
|
||||
if (clazz != null && clazz.modality == Modality.FINAL && clazz.source is PsiSourceElement) {
|
||||
|
||||
fun MemberScope.findCreatorField() = getContributedVariables(
|
||||
Name.identifier("CREATOR"), NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS
|
||||
).firstOrNull()
|
||||
|
||||
val creatorVar = when (clazz) {
|
||||
is JavaClassDescriptor -> clazz.staticScope.findCreatorField()
|
||||
else -> clazz.companionObjectDescriptor?.unsubstitutedMemberScope?.findCreatorField()
|
||||
?.takeIf { it.annotations.hasAnnotation(FqName(JvmField::class.java.name)) }
|
||||
}
|
||||
|
||||
val creatorAsmType = when {
|
||||
creatorVar != null -> typeMapper.mapTypeSafe(creatorVar.type, forceBoxed = true)
|
||||
clazz.isParcelize -> Type.getObjectType(asmType.internalName + "\$Creator")
|
||||
else -> null
|
||||
}
|
||||
|
||||
creatorAsmType?.let { wrapToNullAwareIfNeeded(type, EfficientParcelableParcelSerializer(asmType)) }
|
||||
?: GenericParcelableParcelSerializer(asmType, context.containerClassType)
|
||||
} else {
|
||||
GenericParcelableParcelSerializer(asmType, context.containerClassType)
|
||||
}
|
||||
}
|
||||
|
||||
type.isSerializable() -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeSerializable", "(Ljava/io/Serializable;)V"),
|
||||
Method("readSerializable", "()Ljava/io/Serializable;")
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (strict && !type.annotations.hasAnnotation(RAW_VALUE_ANNOTATION_FQNAME))
|
||||
throw IllegalArgumentException("Illegal type")
|
||||
else
|
||||
GenericParcelSerializer(asmType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapToNullAwareIfNeeded(type: KotlinType, serializer: ParcelSerializer): ParcelSerializer {
|
||||
return 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.isException() = matchesFqNameWithSupertypes("java.lang.Exception")
|
||||
private fun KotlinType.isIBinder() = matchesFqNameWithSupertypes("android.os.IBinder")
|
||||
private fun KotlinType.isIInterface() = matchesFqNameWithSupertypes("android.os.IInterface")
|
||||
private fun KotlinType.isCharSequence() = matchesFqName("kotlin.CharSequence") || matchesFqName("java.lang.CharSequence")
|
||||
|
||||
private fun KotlinType.isSerializable() = matchesFqNameWithSupertypes("java.io.Serializable")
|
||||
|| matchesFqNameWithSupertypes("kotlin.Function")
|
||||
|
||||
private fun KotlinType.isNamedObject(): Boolean {
|
||||
val classDescriptor = constructor.declarationDescriptor as? ClassDescriptor ?: return false
|
||||
if (!classDescriptor.visibility.isVisibleOutside()) return false
|
||||
if (DescriptorUtils.isAnonymousObject(classDescriptor)) return false
|
||||
return classDescriptor.kind == ClassKind.OBJECT
|
||||
}
|
||||
|
||||
private fun KotlinType.isEnum() = (constructor.declarationDescriptor as? ClassDescriptor)?.kind == ClassKind.ENUM_CLASS
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KotlinType.isParcelable() = matchesFqNameWithSupertypes("android.os.Parcelable")
|
||||
|
||||
private fun KotlinType.matchesFqName(fqName: String): Boolean {
|
||||
return this.constructor.declarationDescriptor?.fqNameSafe?.asString() == fqName
|
||||
}
|
||||
|
||||
private fun KotlinType.matchesFqNameWithSupertypes(fqName: String): Boolean {
|
||||
if (this.matchesFqName(fqName)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return TypeUtils.getAllSupertypes(this).any { it.matchesFqName(fqName) }
|
||||
}
|
||||
+834
@@ -0,0 +1,834 @@
|
||||
/*
|
||||
* 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.parcelize.serializers
|
||||
|
||||
import kotlinx.parcelize.Parceler
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.FrameMap
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.codegen.useTmpVar
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.parcelize.serializers.BoxedPrimitiveTypeParcelSerializer.Companion.BOXED_VALUE_METHOD_NAMES
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
internal val PARCEL_TYPE = Type.getObjectType("android/os/Parcel")
|
||||
internal val PARCELER_TYPE = Type.getObjectType(Parceler::class.java.name.replace(".", "/"))
|
||||
|
||||
internal class GenericParcelSerializer(override val asmType: Type) : ParcelSerializer {
|
||||
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)
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
internal class TypeParcelerParcelSerializer(
|
||||
override val asmType: Type,
|
||||
private val parcelerType: KotlinType,
|
||||
private val typeMapper: KotlinTypeMapper
|
||||
) : ParcelSerializer {
|
||||
private val parcelerAsmType = typeMapper.mapType(parcelerType)
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
// -> parcel, value(?)
|
||||
boxTypeIfNeeded(v) // -> parcel, (boxed)value
|
||||
|
||||
v.swap() // -> value, parcel
|
||||
putObjectOrClassInstanceOnStack(parcelerType, parcelerAsmType, typeMapper, v) // -> value, parcel, parceler
|
||||
v.dupX2() // -> parceler, value, parcel, parceler
|
||||
v.pop() // -> parceler, value, parcel
|
||||
v.load(2, Type.INT_TYPE) // -> parceler, value, parcel, flags
|
||||
v.invokeinterface(PARCELER_TYPE.internalName, "write", "(Ljava/lang/Object;Landroid/os/Parcel;I)V")
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
// -> parcel
|
||||
putObjectOrClassInstanceOnStack(parcelerType, parcelerAsmType, typeMapper, v) // -> parcel, parceler
|
||||
v.swap() // -> parceler, parcel
|
||||
v.invokeinterface(PARCELER_TYPE.internalName, "create", "(Landroid/os/Parcel;)Ljava/lang/Object;") // -> obj
|
||||
unboxTypeIfNeeded(v)
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
|
||||
private fun handleSpecialBoxingCases(v: InstructionAdapter): Type? {
|
||||
assert(asmType.sort != Type.METHOD)
|
||||
|
||||
if (asmType.sort == Type.OBJECT || asmType.sort == Type.ARRAY) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (asmType == Type.VOID_TYPE) {
|
||||
v.pop()
|
||||
v.aconst(null)
|
||||
return null
|
||||
}
|
||||
|
||||
return AsmUtil.boxType(asmType)
|
||||
}
|
||||
|
||||
private fun boxTypeIfNeeded(v: InstructionAdapter) {
|
||||
val boxedType = handleSpecialBoxingCases(v) ?: return
|
||||
v.invokestatic(boxedType.internalName, "valueOf", "(${asmType.descriptor})${boxedType.descriptor}", false)
|
||||
}
|
||||
|
||||
private fun unboxTypeIfNeeded(v: InstructionAdapter) {
|
||||
val boxedType = handleSpecialBoxingCases(v) ?: return
|
||||
val getValueMethodName = BOXED_VALUE_METHOD_NAMES.getValue(boxedType.internalName)
|
||||
v.castIfNeeded(boxedType)
|
||||
v.invokevirtual(boxedType.internalName, getValueMethodName, "()${asmType.descriptor}", 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.load(1, PARCEL_TYPE) // -> length, arr, index, arr, index, parcel
|
||||
v.dupX2() // -> length, arr, index, parcel, arr, index, parcel
|
||||
v.pop() // -> length, arr, index, parcel, arr, index
|
||||
v.aload(elementSerializer.asmType) // -> length, arr, index, parcel, obj
|
||||
v.castIfNeeded(elementSerializer.asmType)
|
||||
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,
|
||||
frameMap: FrameMap
|
||||
) : AbstractCollectionParcelSerializer(asmType, elementSerializer, frameMap) {
|
||||
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,
|
||||
frameMap: FrameMap
|
||||
) : AbstractCollectionParcelSerializer(asmType, elementSerializer, frameMap) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class AbstractCollectionParcelSerializer(
|
||||
final override val asmType: Type,
|
||||
protected val elementSerializer: ParcelSerializer,
|
||||
private val frameMap: FrameMap
|
||||
) : ParcelSerializer {
|
||||
protected val collectionType: Type = Type.getObjectType(
|
||||
when (asmType.internalName) {
|
||||
"java/util/List" -> "java/util/ArrayList"
|
||||
"java/util/Set" -> "java/util/LinkedHashSet"
|
||||
"java/util/SortedSet" -> "java/util/TreeSet"
|
||||
"java/util/NavigableSet" -> "java/util/TreeSet"
|
||||
"java/util/Map" -> "java/util/LinkedHashMap"
|
||||
"java/util/SortedMap" -> "java/util/TreeMap"
|
||||
"java/util/NavigableMap" -> "java/util/TreeMap"
|
||||
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.load(1, PARCEL_TYPE) // iterator, iterator, parcel
|
||||
v.swap() // iterator, parcel, iterator
|
||||
v.invokeinterface("java/util/Iterator", "next", "()Ljava/lang/Object;") // -> iterator, parcel, obj
|
||||
|
||||
doWriteValue(v) // -> iterator
|
||||
|
||||
v.goTo(labelIteratorLoop)
|
||||
|
||||
v.visitLabel(labelReturn)
|
||||
v.pop()
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
frameMap.useTmpVar(Type.INT_TYPE) { sizeVarIndex ->
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> size
|
||||
v.store(sizeVarIndex, Type.INT_TYPE)
|
||||
|
||||
v.anew(collectionType) // -> list
|
||||
v.dup() // -> list, list
|
||||
|
||||
if (hasConstructorWithCapacity) {
|
||||
v.load(sizeVarIndex, Type.INT_TYPE)
|
||||
v.invokespecial(collectionType.internalName, "<init>", "(I)V", false) // -> list
|
||||
} else {
|
||||
v.invokespecial(collectionType.internalName, "<init>", "()V", false) // -> list
|
||||
}
|
||||
|
||||
v.load(sizeVarIndex, Type.INT_TYPE) // -> list, size
|
||||
}
|
||||
|
||||
val nextLoopIteration = Label()
|
||||
val loopIsOver = Label()
|
||||
|
||||
v.visitLabel(nextLoopIteration)
|
||||
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.goTo(nextLoopIteration)
|
||||
|
||||
v.visitLabel(loopIsOver)
|
||||
v.swap() // -> list, size
|
||||
v.pop()
|
||||
}
|
||||
}
|
||||
|
||||
internal class SparseArrayParcelSerializer(
|
||||
override val asmType: Type,
|
||||
private val valueSerializer: ParcelSerializer,
|
||||
private val frameMap: FrameMap
|
||||
) : ParcelSerializer {
|
||||
private val valueType = (valueSerializer as? PrimitiveTypeParcelSerializer)?.asmType ?: Type.getObjectType("java/lang/Object")
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
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) {
|
||||
frameMap.useTmpVar(Type.INT_TYPE) { sizeVarIndex ->
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> size
|
||||
v.store(sizeVarIndex, Type.INT_TYPE) // -> (empty)
|
||||
|
||||
v.anew(asmType) // -> arr
|
||||
v.dup() // -> arr, arr
|
||||
v.load(sizeVarIndex, Type.INT_TYPE) // -> arr, arr, size
|
||||
v.invokespecial(asmType.internalName, "<init>", "(I)V", false) // -> arr
|
||||
|
||||
v.load(sizeVarIndex, Type.INT_TYPE) // -> arr, size
|
||||
}
|
||||
|
||||
val nextLoopIteration = Label()
|
||||
val loopIsOver = Label()
|
||||
|
||||
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 ObjectParcelSerializer(
|
||||
override val asmType: Type,
|
||||
private val type: KotlinType,
|
||||
private val typeMapper: KotlinTypeMapper
|
||||
) : ParcelSerializer {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.pop2()
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.pop()
|
||||
putObjectOrClassInstanceOnStack(type, asmType, typeMapper, v)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ZeroParameterClassSerializer(
|
||||
override val asmType: Type,
|
||||
type: KotlinType
|
||||
) : ParcelSerializer {
|
||||
private val clazz = type.constructor.declarationDescriptor as ClassDescriptor
|
||||
|
||||
init {
|
||||
assert(clazz.kind == ClassKind.CLASS)
|
||||
}
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.pop2()
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.pop()
|
||||
|
||||
val constructor = clazz.unsubstitutedPrimaryConstructor
|
||||
assert(constructor == null || constructor.valueParameters.isEmpty())
|
||||
v.anew(asmType)
|
||||
v.dup()
|
||||
v.invokespecial(asmType.internalName, "<init>", "()V", false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun putObjectOrClassInstanceOnStack(type: KotlinType, asmType: Type, typeMapper: KotlinTypeMapper, v: InstructionAdapter) {
|
||||
val clazz = type.constructor.declarationDescriptor as? ClassDescriptor
|
||||
|
||||
if (clazz != null) {
|
||||
if (clazz.isCompanionObject) {
|
||||
val outerClass = clazz.containingDeclaration as? ClassDescriptor
|
||||
if (outerClass != null) {
|
||||
v.getstatic(typeMapper.mapType(outerClass.defaultType).internalName, clazz.name.asString(), asmType.descriptor)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v.getstatic(asmType.internalName, "INSTANCE", asmType.descriptor)
|
||||
}
|
||||
|
||||
internal class EnumParcelSerializer(override val asmType: Type) : ParcelSerializer {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.invokevirtual("java/lang/Enum", "name", "()Ljava/lang/String;", false)
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeString", "(Ljava/lang/String;)V", false)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readString", "()Ljava/lang/String;", false)
|
||||
v.aconst(asmType)
|
||||
v.swap()
|
||||
v.invokestatic("java/lang/Enum", "valueOf", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;", false)
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
internal class CharSequenceParcelSerializer(override val asmType: Type) : ParcelSerializer {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
// -> parcel, seq
|
||||
v.swap() // -> seq, parcel
|
||||
v.aconst(0) // -> seq, parcel, flags
|
||||
v.invokestatic("android/text/TextUtils", "writeToParcel", "(Ljava/lang/CharSequence;Landroid/os/Parcel;I)V", false)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
// -> parcel
|
||||
v.getstatic("android/text/TextUtils", "CHAR_SEQUENCE_CREATOR", "Landroid/os/Parcelable\$Creator;") // -> parcel, creator
|
||||
v.swap() // -> creator, parcel
|
||||
v.invokeinterface("android/os/Parcelable\$Creator", "createFromParcel", "(Landroid/os/Parcel;)Ljava/lang/Object;")
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
internal class EfficientParcelableParcelSerializer(override val asmType: Type) : ParcelSerializer {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
// -> parcel, parcelable
|
||||
v.swap() // -> parcelable, parcel
|
||||
v.aconst(0) // -> parcelable, parcel, flags
|
||||
v.invokeinterface("android/os/Parcelable", "writeToParcel", "(Landroid/os/Parcel;I)V")
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
// -> parcel
|
||||
v.getstatic(asmType.internalName, "CREATOR", "Landroid/os/Parcelable\$Creator;") // -> parcel, creator
|
||||
v.swap() // -> creator, parcel
|
||||
v.invokeinterface("android/os/Parcelable\$Creator", "createFromParcel", "(Landroid/os/Parcel;)Ljava/lang/Object;")
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
internal class GenericParcelableParcelSerializer(override val asmType: Type, private val containerClassType: Type) : ParcelSerializer {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
// -> parcel, parcelable
|
||||
v.load(2, Type.INT_TYPE) // -> parcel, parcelable, flags
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeParcelable", "(Landroid/os/Parcelable;I)V", false)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
// -> parcel
|
||||
v.aconst(containerClassType) // -> parcel, type
|
||||
v.invokevirtual("java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false) // -> parcel, classloader
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readParcelable", "(Ljava/lang/ClassLoader;)Landroid/os/Parcelable;", false)
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
internal class NullAwareParcelSerializerWrapper(private 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)
|
||||
}
|
||||
}
|
||||
|
||||
internal class PrimitiveArrayParcelSerializer(
|
||||
override val asmType: Type
|
||||
) : ParcelSerializer {
|
||||
private val methodNameBase = when (asmType.elementType) {
|
||||
Type.INT_TYPE -> "Int"
|
||||
Type.BOOLEAN_TYPE -> "Boolean"
|
||||
Type.BYTE_TYPE -> "Byte"
|
||||
Type.CHAR_TYPE -> "Char"
|
||||
Type.DOUBLE_TYPE -> "Double"
|
||||
Type.FLOAT_TYPE -> "Float"
|
||||
Type.LONG_TYPE -> "Long"
|
||||
else -> error("Unsupported type ${asmType.elementType.descriptor}")
|
||||
}
|
||||
|
||||
private val writeMethod = Method("write${methodNameBase}Array", "(${asmType.descriptor})V")
|
||||
private val createArrayMethod = Method("create${methodNameBase}Array", "()${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, createArrayMethod.name, createArrayMethod.signature, false)
|
||||
}
|
||||
}
|
||||
|
||||
/** 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(private 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()
|
||||
|
||||
internal 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"))
|
||||
|
||||
private val unboxedSerializer = PrimitiveTypeParcelSerializer.getInstance(unboxedType)
|
||||
private val typeValueMethodName = BOXED_VALUE_METHOD_NAMES[asmType.internalName] ?: error("Boxed method name not found for $asmType")
|
||||
|
||||
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
|
||||
Type.BOOLEAN_TYPE -> BooleanParcelSerializer
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
object BooleanParcelSerializer : PrimitiveTypeParcelSerializer(Type.BOOLEAN_TYPE) {
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
super.readValue(v)
|
||||
|
||||
val falseLabel = Label()
|
||||
val conditionIsOver = Label()
|
||||
|
||||
v.ifeq(falseLabel)
|
||||
v.iconst(1)
|
||||
v.goTo(conditionIsOver)
|
||||
|
||||
v.visitLabel(falseLabel)
|
||||
v.iconst(0)
|
||||
|
||||
v.visitLabel(conditionIsOver)
|
||||
}
|
||||
}
|
||||
|
||||
private val writeMethod = WRITE_METHOD_NAMES[asmType] ?: error("Write method not found for $asmType")
|
||||
private val readMethod = READ_METHOD_NAMES[asmType] ?: error("Read method not found for $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)
|
||||
|
||||
v.visitLabel(labelNull)
|
||||
v.pop()
|
||||
v.aconst(0)
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize.serializers
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL
|
||||
import org.jetbrains.kotlin.parcelize.isParcelize
|
||||
import java.io.FileDescriptor
|
||||
|
||||
interface ParcelizeExtensionBase {
|
||||
companion object {
|
||||
val FILE_DESCRIPTOR_FQNAME = FqName(FileDescriptor::class.java.canonicalName)
|
||||
val CREATOR_NAME = Name.identifier("CREATOR")
|
||||
val ALLOWED_CLASS_KINDS = listOf(ClassKind.CLASS, ClassKind.OBJECT, ClassKind.ENUM_CLASS)
|
||||
}
|
||||
|
||||
fun ClassDescriptor.hasCreatorField(): Boolean {
|
||||
val companionObject = companionObjectDescriptor ?: return false
|
||||
|
||||
if (companionObject.name == CREATOR_NAME) {
|
||||
return true
|
||||
}
|
||||
|
||||
return companionObject.unsubstitutedMemberScope
|
||||
.getContributedVariables(CREATOR_NAME, NoLookupLocation.FROM_BACKEND)
|
||||
.isNotEmpty()
|
||||
}
|
||||
|
||||
val ClassDescriptor.isParcelizeClassDescriptor get() = kind in ALLOWED_CLASS_KINDS && isParcelize
|
||||
|
||||
fun ClassDescriptor.hasSyntheticDescribeContents() = hasParcelizeSyntheticMethod(DESCRIBE_CONTENTS)
|
||||
|
||||
fun ClassDescriptor.hasSyntheticWriteToParcel() = hasParcelizeSyntheticMethod(WRITE_TO_PARCEL)
|
||||
|
||||
fun ClassDescriptor.findFunction(componentKind: ParcelizeSyntheticComponent.ComponentKind): SimpleFunctionDescriptor? {
|
||||
return unsubstitutedMemberScope
|
||||
.getContributedFunctions(Name.identifier(componentKind.methodName), NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS)
|
||||
.firstOrNull { (it as? ParcelizeSyntheticComponent)?.componentKind == componentKind }
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.hasParcelizeSyntheticMethod(componentKind: ParcelizeSyntheticComponent.ComponentKind): Boolean {
|
||||
val methodName = Name.identifier(componentKind.methodName)
|
||||
|
||||
val writeToParcelMethods = unsubstitutedMemberScope
|
||||
.getContributedFunctions(methodName, NoLookupLocation.FROM_BACKEND)
|
||||
.filter { it is ParcelizeSyntheticComponent && it.componentKind == componentKind }
|
||||
|
||||
return writeToParcelMethods.size == 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
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,
|
||||
|
||||
val nboo: Boolean?, val nc: Char?, val nbyt: Byte?, val ns: Short?,
|
||||
val ni: Int?, val nf: Float?, val nl: Long?, val nd: Double?,
|
||||
|
||||
val jboo: java.lang.Boolean, val jc: java.lang.Character, val jbyt: java.lang.Byte, val js: java.lang.Short,
|
||||
val ji: java.lang.Integer, val jf: java.lang.Float, val jl: java.lang.Long, val jd: java.lang.Double,
|
||||
|
||||
val njboo: java.lang.Boolean?, val njc: java.lang.Character?, val njbyt: java.lang.Byte?, val njs: java.lang.Short?,
|
||||
val nji: java.lang.Integer?, val njf: java.lang.Float?, val njl: java.lang.Long?, val njd: java.lang.Double?
|
||||
) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = PrimitiveTypes(
|
||||
true, '#', 3.toByte(), 10.toShort(), -300, -5.0f, Long.MAX_VALUE, 3.14,
|
||||
true, '#', 3.toByte(), 10.toShort(), -300, -5.0f, Long.MAX_VALUE, 3.14,
|
||||
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,
|
||||
10L as java.lang.Long, 3.14 as java.lang.Double,
|
||||
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,
|
||||
10L as java.lang.Long, 3.14 as java.lang.Double
|
||||
)
|
||||
val second = PrimitiveTypes(
|
||||
false, '\n', Byte.MIN_VALUE, Short.MIN_VALUE,
|
||||
Int.MIN_VALUE, Float.POSITIVE_INFINITY, Long.MAX_VALUE, Double.NEGATIVE_INFINITY,
|
||||
null, null, null, null, null, null, null, null,
|
||||
false as java.lang.Boolean, '\n' as java.lang.Character,
|
||||
Byte.MIN_VALUE as java.lang.Byte, Short.MIN_VALUE as java.lang.Short,
|
||||
Int.MIN_VALUE as java.lang.Integer, Float.POSITIVE_INFINITY as java.lang.Float,
|
||||
java.lang.Long(Long.MAX_VALUE), java.lang.Double(Double.NEGATIVE_INFINITY),
|
||||
null, null, null, null, null, null, null, null
|
||||
)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
second.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<PrimitiveTypes>(parcel)
|
||||
val second2 = readFromParcel<PrimitiveTypes>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
assert(second == second2)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
@Parcelize
|
||||
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)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
@Parcelize
|
||||
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)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// IGNORE_BACKEND: JVM
|
||||
// See KT-38103
|
||||
// There is no such thing as a readStrongInterface method to deserialize arbitrary IIinterface implementations
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Binder
|
||||
import android.os.IBinder
|
||||
import android.os.IInterface
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.io.Serializable
|
||||
|
||||
class MockBinder : Binder(), Serializable
|
||||
|
||||
@Parcelize
|
||||
class MockIInterface : IInterface, Parcelable {
|
||||
override fun asBinder(): IBinder = MockBinder()
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
class ServiceContainer(
|
||||
val binder: MockBinder,
|
||||
val iinterface: MockIInterface,
|
||||
val binderArray: Array<IBinder>,
|
||||
val binderList: List<IBinder>
|
||||
) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = ServiceContainer(MockBinder(), MockIInterface(), arrayOf(MockBinder()), listOf(MockBinder()))
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<ServiceContainer>(parcel)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
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)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<BoxedTypes>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.os.Bundle
|
||||
|
||||
@Parcelize
|
||||
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)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.text.SpannableString
|
||||
|
||||
@Parcelize
|
||||
data class Test(val simple: CharSequence, val spanned: CharSequence) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = Test("John", SpannableString("Smith"))
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(test.simple.toString() == test2.simple.toString())
|
||||
assert(test.spanned.toString() == test2.spanned.toString())
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class User(val firstName: String, val secondName: String, val age: Int) : Parcelable {
|
||||
companion object : Parceler<User> {
|
||||
override fun User.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(firstName)
|
||||
parcel.writeString(secondName)
|
||||
}
|
||||
|
||||
override fun create(parcel: Parcel) = User(parcel.readString(), parcel.readString(), 0)
|
||||
|
||||
override fun newArray(size: Int) = arrayOfNulls<User>(size) as Array<User>
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val user = User("John", "Smith", 20)
|
||||
val user2 = User("Joe", "Bloggs", 30)
|
||||
val array = arrayOf(user, user2)
|
||||
parcel.writeTypedArray(array, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val creator = User::class.java.getDeclaredField("CREATOR").get(null) as Parcelable.Creator<User>
|
||||
val result = parcel.createTypedArray(creator)
|
||||
|
||||
assert(result.size == 2)
|
||||
assert(result[0].firstName == user.firstName)
|
||||
assert(result[1].firstName == user2.firstName)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// IGNORE_BACKEND: JVM
|
||||
// See KT-38105
|
||||
// Throws IllegalAccessError, since the code tries to access the private companion field directly from the generated User$Creator class.
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
data class User(val name: String, val age: Int)
|
||||
|
||||
@Parcelize
|
||||
data class UserParcelable(val user: User) : Parcelable {
|
||||
private companion object : Parceler<UserParcelable> {
|
||||
override fun UserParcelable.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(user.name)
|
||||
}
|
||||
|
||||
override fun create(parcel: Parcel) = UserParcelable(User(parcel.readString(), 0))
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val userParcelable = UserParcelable(User("John", 20))
|
||||
userParcelable.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val userParcelable2 = readFromParcel<UserParcelable>(parcel)
|
||||
|
||||
assert(userParcelable.user.name == userParcelable2.user.name)
|
||||
assert(userParcelable2.user.age == 0)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.util.SparseBooleanArray
|
||||
|
||||
object Parceler1 : Parceler<Int> {
|
||||
override fun create(parcel: Parcel) = -parcel.readInt()
|
||||
|
||||
override fun Int.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeInt(this)
|
||||
}
|
||||
}
|
||||
|
||||
object Parceler2 : Parceler<Int> {
|
||||
override fun create(parcel: Parcel) = parcel.readString().length
|
||||
|
||||
override fun Int.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString("Abc")
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
@TypeParceler<Int, Parceler1>
|
||||
data class Ints(val a: Int, @TypeParceler<Int, Parceler2> val b: Int, val c: @WriteWith<Parceler2> Int) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = Ints(1, 1, 1)
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<Ints>(parcel)
|
||||
|
||||
assert(test2.a == -test.a)
|
||||
assert(test2.b == -test.b)
|
||||
assert(test2.c == 3)
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
object Parceler1 : Parceler<Int> {
|
||||
override fun create(parcel: Parcel) = -parcel.readInt()
|
||||
|
||||
override fun Int.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeInt(this)
|
||||
}
|
||||
}
|
||||
|
||||
object Parceler2 : Parceler<Long> {
|
||||
override fun create(parcel: Parcel) = parcel.readString().length.toLong()
|
||||
|
||||
override fun Long.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString("Abc")
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class Test(
|
||||
val a: Int,
|
||||
@TypeParceler<Int, Parceler1> val b: Int,
|
||||
@TypeParceler<Long, Parceler2> val c: Long,
|
||||
@TypeParceler<Int, Parceler1> val d: List<Int>,
|
||||
@TypeParceler<Long, Parceler2> val e: LongArray
|
||||
) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = Test(5, 5, 50L, listOf(1, 2, 3), longArrayOf(3, 2, 1))
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<Test>(parcel)
|
||||
|
||||
println(test.toString())
|
||||
println(test2.toString())
|
||||
|
||||
with (test) {
|
||||
assert(a == 5 && b == 5 && c == 50L && d == listOf(1, 2, 3) && Arrays.equals(e, longArrayOf(3, 2, 1)))
|
||||
}
|
||||
|
||||
with (test2) {
|
||||
assert(a == 5 && b == -5 && c == 3L && d == listOf(-1, -2, -3) && Arrays.equals(e, longArrayOf(3, 3, 3)))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
object Parceler1 : Parceler<String> {
|
||||
override fun create(parcel: Parcel) = parcel.readInt().toString()
|
||||
|
||||
override fun String.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeInt(length)
|
||||
}
|
||||
}
|
||||
|
||||
typealias Parceler2 = Parceler1
|
||||
|
||||
object Parceler3 : Parceler<String> {
|
||||
override fun create(parcel: Parcel) = parcel.readString().toUpperCase()
|
||||
|
||||
override fun String.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(this)
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
@TypeParceler<String, Parceler2>
|
||||
data class Test(
|
||||
val a: String,
|
||||
@TypeParceler<String, Parceler1> val b: String,
|
||||
@TypeParceler<String, Parceler3> val c: CharSequence,
|
||||
val d: @WriteWith<Parceler3> String
|
||||
) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = Test("Abc", "Abc", "Abc", "Abc")
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(test.a == "Abc" && test.b == "Abc" && test.c == "Abc" && test.d == "Abc")
|
||||
assert(test2.a == "3" && test2.b == "3" && test2.c == "Abc" && test2.d == "ABC")
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// IGNORE_BACKEND: JVM
|
||||
// See KT-38107
|
||||
// The JVM backend is missing support for custom parcelers in List<String>
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
object Parceler1 : Parceler<String> {
|
||||
override fun create(parcel: Parcel) = parcel.readInt().toString()
|
||||
|
||||
override fun String.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeInt(length)
|
||||
}
|
||||
}
|
||||
|
||||
object Parceler2 : Parceler<List<String>> {
|
||||
override fun create(parcel: Parcel) = listOf(parcel.readString())
|
||||
|
||||
override fun List<String>.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(this.joinToString(","))
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class Test(
|
||||
val a: String,
|
||||
val b: @WriteWith<Parceler1> String,
|
||||
val c: List<@WriteWith<Parceler1> String>,
|
||||
val d: @WriteWith<Parceler2> List<String>,
|
||||
val e: @WriteWith<Parceler2> List<@WriteWith<Parceler1> String>
|
||||
) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = Test("Abc", "Abc", listOf("A", "bc"), listOf("A", "bc"), listOf("A", "bc"))
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<Test>(parcel)
|
||||
|
||||
with (test) {
|
||||
assert(a == "Abc" && b == "Abc" && c == listOf("A", "bc") && d == listOf("A", "bc") && e == listOf("A", "bc"))
|
||||
}
|
||||
|
||||
with (test2) {
|
||||
assert(a == "Abc" && b == "3" && c == listOf("1", "2") && d == listOf("A,bc") && e == listOf("A,bc"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// IGNORE_BACKEND: JVM
|
||||
// See KT-38105
|
||||
// Throws IllegalAccessError, since the code tries to access the private companion field directly from the generated User$Creator class.
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class User(val firstName: String, val secondName: String, val age: Int) : Parcelable {
|
||||
private companion object : Parceler<User> {
|
||||
override fun User.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(firstName)
|
||||
parcel.writeString(secondName)
|
||||
}
|
||||
|
||||
override fun create(parcel: Parcel) = User(parcel.readString(), parcel.readString(), 0)
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val user = User("John", "Smith", 20)
|
||||
user.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val user2 = readFromParcel<User>(parcel)
|
||||
|
||||
assert(user.firstName == user2.firstName)
|
||||
assert(user.secondName == user2.secondName)
|
||||
assert(user2.age == 0)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
enum class Color : Parcelable { BLACK, WHITE }
|
||||
|
||||
@Parcelize
|
||||
object Obj : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val black = Color.BLACK
|
||||
val obj = Obj
|
||||
|
||||
black.writeToParcel(parcel, 0)
|
||||
obj.writeToParcel(parcel, 0)
|
||||
|
||||
println(black)
|
||||
println(obj)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val black2 = readFromParcel<Color>(parcel)
|
||||
val obj2 = readFromParcel<Obj>(parcel)
|
||||
|
||||
println(black2)
|
||||
println(obj2)
|
||||
|
||||
assert(black2 == black)
|
||||
assert(obj2 != null)
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
enum class Color {
|
||||
BLACK, WHITE
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class Test(val name: String, val color: Color) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = Test("John", Color.WHITE)
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<Test>(parcel)
|
||||
assert(test == test2)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// IGNORE_BACKEND: JVM
|
||||
// Parcel.readException throws the exception it reads and only supports a small number of exception types.
|
||||
// If we have to parcel an exception we should instead use read/writeSerializable (compare KT-31830)
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
class ExceptionContainer(val exn: Exception) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = ExceptionContainer(java.lang.RuntimeException("Don't throw me."))
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<ExceptionContainer>(parcel)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class Test(val callback: () -> Int = { 0 }, val suspendCallback: suspend () -> Int = { 0 }) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = Test({ 1 }, { 1 })
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(test.callback() == 1)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
@Parcelize
|
||||
data class Film(val genres: Array<Int>) : Parcelable {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Film
|
||||
|
||||
if (!Arrays.equals(genres, other.genres)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return Arrays.hashCode(genres)
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val film = Film(arrayOf(3, 5, 7))
|
||||
film.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val film2 = readFromParcel<Film>(parcel)
|
||||
assert(film == film2)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// This issue affects AIDL generated files, as reported in KT-25807
|
||||
// WITH_RUNTIME
|
||||
// FILE: J.java
|
||||
import android.os.Parcel;
|
||||
import test.K;
|
||||
|
||||
public class J {
|
||||
public static K readParcel(Parcel parcel) {
|
||||
return K.CREATOR.createFromParcel(parcel);
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: test.kt
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class K(val x: Int) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = K(0)
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val second = J.readParcel(parcel)
|
||||
assert(first == second)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.io.Serializable
|
||||
|
||||
class JHelp(var j1: String) {
|
||||
val j2 = 9
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
class J(val j: @RawValue JHelp) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = J(JHelp("A"))
|
||||
|
||||
var exceptionCaught = false
|
||||
try {
|
||||
test.writeToParcel(parcel, 0)
|
||||
} catch (e: RuntimeException) {
|
||||
if (e.message!!.contains("Parcel: unable to marshal value test.JHelp")) {
|
||||
exceptionCaught = true
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
if (!exceptionCaught) {
|
||||
error("Exception should be thrown")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.io.Serializable
|
||||
|
||||
interface IJHelp {
|
||||
val j1: String
|
||||
}
|
||||
|
||||
class JHelp(override var j1: String): IJHelp, Serializable {
|
||||
val j2 = 9
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
class J(val j: @RawValue JHelp) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = J(JHelp("A"))
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<J>(parcel)
|
||||
|
||||
assert(test.j.j1 == test2.j.j1)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.io.Serializable
|
||||
|
||||
class MHelp(var m1: String): Serializable {
|
||||
val m2 = 9
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
class M(val m: @RawValue MHelp) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = M(MHelp("A"))
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<M>(parcel)
|
||||
|
||||
assert(test.m.m1 == test2.m.m1)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
@Parcelize
|
||||
data class Test(val a: LongArray, val b: List<Long>) : 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 (b != other.b) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode() = Arrays.hashCode(a)
|
||||
}
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Test(longArrayOf(1, 2, 3, 4, 5), listOf(1, 2, 3, 4))
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// IGNORE_BACKEND: JVM
|
||||
// See KT-38106
|
||||
// This feature regressed with the fix for KT-22576
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
enum class ParcelableEnum : Parcelable {
|
||||
ONE, TWO, THREE;
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeInt(ordinal)
|
||||
}
|
||||
|
||||
override fun describeContents() = 0
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val CREATOR: Parcelable.Creator<ParcelableEnum> = object : Parcelable.Creator<ParcelableEnum> {
|
||||
override fun createFromParcel(parcel: Parcel) = ParcelableEnum.ONE
|
||||
override fun newArray(size: Int) = arrayOfNulls<ParcelableEnum>(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
class Test(val parcelableEnum: ParcelableEnum): Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Test(ParcelableEnum.THREE)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first.parcelableEnum == ParcelableEnum.THREE)
|
||||
assert(first2.parcelableEnum == ParcelableEnum.ONE)
|
||||
assert(first != first2)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
fun box() = doTest { creator ->
|
||||
assert(creator.newArray(5) != null)
|
||||
}
|
||||
|
||||
fun doTest(work: (Parcelable.Creator<DummyParcelable>) -> Unit): String {
|
||||
val dummy = DummyParcelable(42)
|
||||
|
||||
val clazz = dummy.javaClass
|
||||
val field = clazz.getDeclaredField("CREATOR")
|
||||
val creator = field.get(dummy) as Parcelable.Creator<DummyParcelable>
|
||||
|
||||
val parcel = Parcel.obtain()
|
||||
dummy.writeToParcel(parcel, 0)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
work(creator)
|
||||
return "OK"
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class DummyParcelable(val int: Int): Parcelable
|
||||
@@ -0,0 +1,29 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
class User : Parcelable
|
||||
|
||||
@Parcelize
|
||||
class User2() : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val user = User()
|
||||
val user2 = User2()
|
||||
|
||||
user.writeToParcel(parcel, 0)
|
||||
user2.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
readFromParcel<User>(parcel)
|
||||
readFromParcel<User2>(parcel)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// IGNORE_BACKEND: JVM
|
||||
// Fails with a VerifyError in Foo.writeToParcel
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.util.SparseArray
|
||||
|
||||
@Parcelize
|
||||
data class PInt(val x: Int) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class Foo(val values: SparseArray<SparseArray<Parcelable>>) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val pint = PInt(0)
|
||||
val sarray = SparseArray<Parcelable>()
|
||||
sarray.put(0, pint)
|
||||
val sarray2 = SparseArray<SparseArray<Parcelable>>()
|
||||
sarray2.put(1, sarray)
|
||||
val foo = Foo(sarray2)
|
||||
|
||||
foo.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val foo2 = readFromParcel<Foo>(parcel)
|
||||
assert(foo2.values.size() == 1)
|
||||
assert(foo2.values.get(1) != null) // SparseArray.contains was only added in Android R
|
||||
assert(foo2.values.get(1).size() == 1)
|
||||
assert(foo2.values.get(1).get(0) != null)
|
||||
assert(foo2.values.get(1).get(0) == pint)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// IGNORE_BACKEND: JVM
|
||||
// StackOverflowError caused by infinite loop in MyObject.writeToParcel
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
private object MyObject : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
MyObject.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
readFromParcel<MyObject>(parcel)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize class TestParcel : Parcelable {
|
||||
companion object : Parceler<TestParcel> {
|
||||
override fun create(parcel: Parcel): TestParcel {
|
||||
return TestParcel()
|
||||
}
|
||||
override fun TestParcel.write(parcel: Parcel, flags: Int) {}
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
TestParcel()
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// WITH_RUNTIME
|
||||
// FULL_JDK
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.*
|
||||
|
||||
@Parcelize
|
||||
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>,
|
||||
val j: NavigableSet<String>,
|
||||
val k: SortedSet<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" },
|
||||
j = TreeSet<String>().apply { this += "J" },
|
||||
k = TreeSet<String>().apply { this += "K" }
|
||||
)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
assert((first.d as LinkedList<*>).size == 1)
|
||||
assert((first2.h as HashSet<*>).size == 1)
|
||||
assert(first2.j is NavigableSet<*>)
|
||||
assert(first2.k is SortedSet<*>)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
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)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
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)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
|
||||
assert(first2.a == listOf("A", "B"))
|
||||
assert(first2.b.size == 3)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// WITH_RUNTIME
|
||||
// FULL_JDK
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.*
|
||||
|
||||
@Parcelize
|
||||
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>,
|
||||
val f: SortedMap<String, String>,
|
||||
val g: NavigableMap<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") },
|
||||
f = TreeMap<String, String>().apply { put("A", "B") },
|
||||
g = TreeMap<String, String>().apply { put("A", "B") }
|
||||
)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
assert((first.c as HashMap<*, *>).size == 1)
|
||||
assert((first2.e as TreeMap<*, *>).size == 1)
|
||||
assert(first2.f is SortedMap<*, *>)
|
||||
assert(first2.g is NavigableMap<*, *>)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
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)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
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)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
@Parcelize
|
||||
class Data(val data: Array<Array<Int>>) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Data(arrayOf(arrayOf(0, 1)))
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val second = readFromParcel<Data>(parcel)
|
||||
assert(second.data.size == 1)
|
||||
assert(Arrays.equals(second.data[0], arrayOf(0, 1)))
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
@Parcelize
|
||||
class Data(val data: List<Array<Int>>) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Data(listOf(arrayOf(0, 1)))
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val second = readFromParcel<Data>(parcel)
|
||||
assert(second.data.size == 1)
|
||||
assert(Arrays.equals(second.data[0], arrayOf(0, 1)))
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
@Parcelize
|
||||
class Data(val data: Map<Array<Int>, Array<Int>>) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Data(mapOf(arrayOf(0) to arrayOf(1)))
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val second = readFromParcel<Data>(parcel)
|
||||
assert(second.data.size == 1)
|
||||
val entry = second.data.entries.single()
|
||||
assert(Arrays.equals(entry.key, arrayOf(0)))
|
||||
assert(Arrays.equals(entry.value, arrayOf(1)))
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
// Starts with A, should be loaded before other classes
|
||||
abstract class AParcelable : Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class P1(val a: String) : AParcelable()
|
||||
|
||||
sealed class Sealed : AParcelable()
|
||||
|
||||
@Parcelize
|
||||
data class Sealed1(val a: Int) : Sealed()
|
||||
|
||||
@Parcelize
|
||||
data class Test(val a: P1, val b: AParcelable, val c: Sealed, val d: Sealed1) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = Test(P1(""), P1("My"), Sealed1(1), Sealed1(5))
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<Test>(parcel)
|
||||
assert(test == test2)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// WITH_RUNTIME
|
||||
// IGNORE_BACKEND: JVM
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.util.SparseArray
|
||||
import java.util.Arrays
|
||||
|
||||
@Parcelize
|
||||
class Data(val data: SparseArray<Array<Int>>) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
var array = SparseArray<Array<Int>>()
|
||||
array.append(0, arrayOf(0, 1))
|
||||
val first = Data(array)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val second = readFromParcel<Data>(parcel)
|
||||
assert(second.data.size() == 1)
|
||||
assert(Arrays.equals(second.data.get(0), arrayOf(0, 1)))
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class User(val firstName: String, val secondName: String, val age: Int) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val user = User("John", "Smith", 20)
|
||||
val user2 = User("Joe", "Bloggs", 30)
|
||||
val array = arrayOf(user, user2)
|
||||
parcel.writeTypedArray(array, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val creator = User::class.java.getDeclaredField("CREATOR").get(null) as Parcelable.Creator<User>
|
||||
val result = parcel.createTypedArray(creator)
|
||||
|
||||
assert(result.size == 2)
|
||||
assert(result[0].firstName == user.firstName)
|
||||
assert(result[1].firstName == user2.firstName)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class Foo(val a: String) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class Test(
|
||||
val str1: String,
|
||||
val str2: String?,
|
||||
val int1: Int,
|
||||
val int2: Int?,
|
||||
val foo: Foo?
|
||||
) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Test("John", "Smith", 20, 30, Foo("a"))
|
||||
val second = Test("A", null, 20, null, null)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
second.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
val second2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
assert(second == second2)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
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)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<Test>(parcel)
|
||||
val second2 = readFromParcel<Test>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
assert(second == second2)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
object Obj1 {
|
||||
object Obj2
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class Test(val o1: Obj1, val o2: Obj1.Obj2, val com: Com) : Parcelable {
|
||||
companion object Com {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = Test(Obj1, Obj1.Obj2, Test.Com)
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<Test>(parcel)
|
||||
assert(test == test2)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
open class Base(val a: String) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
class Inh(var b: Int) : Base(""), Parcelable
|
||||
|
||||
fun box(): String {
|
||||
Inh(0)
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// IGNORE_BACKEND: JVM
|
||||
// See KT-38104
|
||||
// The support for PersistableBundles is broken on JVM.
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.os.BaseBundle
|
||||
import android.os.PersistableBundle
|
||||
|
||||
@Parcelize
|
||||
data class User(val a: PersistableBundle) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val test = User(
|
||||
PersistableBundle().apply { putLong("A", 1L) }
|
||||
)
|
||||
test.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val test2 = readFromParcel<User>(parcel)
|
||||
|
||||
assert(compareBundles(test.a, test2.a))
|
||||
}
|
||||
|
||||
private fun compareBundles(first: BaseBundle, second: BaseBundle): Boolean {
|
||||
if (first.size() != second.size()) return false
|
||||
|
||||
for (key in first.keySet()) {
|
||||
if (first.get(key) != second.get(key)) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
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)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val first2 = readFromParcel<PrimitiveTypes>(parcel)
|
||||
val second2 = readFromParcel<PrimitiveTypes>(parcel)
|
||||
|
||||
assert(first == first2)
|
||||
assert(second == second2)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
sealed class Foo : Parcelable {
|
||||
@Parcelize
|
||||
data class A(val x: Int) : Foo()
|
||||
|
||||
@Parcelize
|
||||
data class B (val x: String) : Foo()
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class Bar(val a: Foo) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Bar(Foo.B("OK"))
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val second = readFromParcel<Bar>(parcel)
|
||||
|
||||
assert(first == second)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
annotation class SerializableLike
|
||||
|
||||
@Parcelize @SerializableLike
|
||||
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)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val user2 = readFromParcel<User>(parcel)
|
||||
assert(user == user2)
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.util.*
|
||||
|
||||
@Parcelize
|
||||
data class Data(val a: String, val b: String) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
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)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.util.SparseBooleanArray
|
||||
|
||||
@Parcelize
|
||||
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)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final User createFromParcel(android.os.Parcel parcel) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
NEW
|
||||
DUP
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readStrongBinder, ()Landroid/os/IBinder;)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, createBinderArray, ()[Landroid/os/IBinder;)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, createBinderArrayList, ()Ljava/util/ArrayList;)
|
||||
CHECKCAST
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, createBinderArrayList, ()Ljava/util/ArrayList;)
|
||||
INVOKESPECIAL (User, <init>, (Landroid/os/IBinder;[Landroid/os/IBinder;Ljava/util/List;Ljava/util/ArrayList;)V)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel source) {
|
||||
LABEL (L0)
|
||||
ALOAD (0)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (User$Creator, createFromParcel, (Landroid/os/Parcel;)LUser;)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public final User[] newArray(int size)
|
||||
|
||||
public java.lang.Object[] newArray(int size)
|
||||
}
|
||||
|
||||
public final class User : java/lang/Object, android/os/Parcelable {
|
||||
public final static android.os.Parcelable$Creator CREATOR
|
||||
|
||||
private final android.os.IBinder binder
|
||||
|
||||
private final android.os.IBinder[] binderArray
|
||||
|
||||
private final java.util.ArrayList binderArrayList
|
||||
|
||||
private final java.util.List binderList
|
||||
|
||||
static void <clinit>()
|
||||
|
||||
public void <init>(android.os.IBinder binder, android.os.IBinder[] binderArray, java.util.List binderList, java.util.ArrayList binderArrayList)
|
||||
|
||||
public int describeContents()
|
||||
|
||||
public final android.os.IBinder getBinder()
|
||||
|
||||
public final android.os.IBinder[] getBinderArray()
|
||||
|
||||
public final java.util.ArrayList getBinderArrayList()
|
||||
|
||||
public final java.util.List getBinderList()
|
||||
|
||||
public void writeToParcel(android.os.Parcel out, int flags) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (out)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (binder, Landroid/os/IBinder;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeStrongBinder, (Landroid/os/IBinder;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (binderArray, [Landroid/os/IBinder;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeBinderArray, ([Landroid/os/IBinder;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (binderList, Ljava/util/List;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeBinderList, (Ljava/util/List;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (binderArrayList, Ljava/util/ArrayList;)
|
||||
CHECKCAST
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeBinderList, (Ljava/util/List;)V)
|
||||
RETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// CURIOUS_ABOUT writeToParcel, createFromParcel
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcelable
|
||||
import android.os.IBinder
|
||||
import android.os.IInterface
|
||||
|
||||
@Parcelize
|
||||
class User(
|
||||
val binder: IBinder,
|
||||
val binderArray: Array<IBinder>,
|
||||
val binderList: List<IBinder>,
|
||||
val binderArrayList: ArrayList<IBinder> // should be serialized using our strategy, not using Parcel.writeBinderList()
|
||||
// There is no readStrongInterface method in Parcel.
|
||||
// val intf: IInterface?
|
||||
) : Parcelable
|
||||
@@ -0,0 +1,126 @@
|
||||
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final User createFromParcel(android.os.Parcel in) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (in)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
LABEL (L1)
|
||||
NEW
|
||||
DUP
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readStrongBinder, ()Landroid/os/IBinder;)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, createBinderArray, ()[Landroid/os/IBinder;)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, createBinderArrayList, ()Ljava/util/ArrayList;)
|
||||
CHECKCAST
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
|
||||
ISTORE (2)
|
||||
NEW
|
||||
DUP
|
||||
ILOAD (2)
|
||||
INVOKESPECIAL (java/util/ArrayList, <init>, (I)V)
|
||||
ILOAD (2)
|
||||
LABEL (L2)
|
||||
DUP_X1
|
||||
IFEQ (L3)
|
||||
DUP
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readStrongBinder, ()Landroid/os/IBinder;)
|
||||
INVOKEVIRTUAL (java/util/ArrayList, add, (Ljava/lang/Object;)Z)
|
||||
POP
|
||||
SWAP
|
||||
LDC (-1)
|
||||
IADD
|
||||
GOTO (L2)
|
||||
LABEL (L3)
|
||||
SWAP
|
||||
POP
|
||||
INVOKESPECIAL (User, <init>, (Landroid/os/IBinder;[Landroid/os/IBinder;Ljava/util/List;Ljava/util/ArrayList;)V)
|
||||
ARETURN
|
||||
LABEL (L4)
|
||||
}
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel p0) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (10)
|
||||
ALOAD (0)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (User$Creator, createFromParcel, (Landroid/os/Parcel;)LUser;)
|
||||
ARETURN
|
||||
}
|
||||
|
||||
public final User[] newArray(int size)
|
||||
|
||||
public java.lang.Object[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class User : java/lang/Object, android/os/Parcelable {
|
||||
public final static android.os.Parcelable$Creator CREATOR
|
||||
|
||||
private final android.os.IBinder binder
|
||||
|
||||
private final android.os.IBinder[] binderArray
|
||||
|
||||
private final java.util.ArrayList binderArrayList
|
||||
|
||||
private final java.util.List binderList
|
||||
|
||||
static void <clinit>()
|
||||
|
||||
public void <init>(android.os.IBinder binder, android.os.IBinder[] binderArray, java.util.List binderList, java.util.ArrayList binderArrayList)
|
||||
|
||||
public int describeContents()
|
||||
|
||||
public final android.os.IBinder getBinder()
|
||||
|
||||
public final android.os.IBinder[] getBinderArray()
|
||||
|
||||
public final java.util.ArrayList getBinderArrayList()
|
||||
|
||||
public final java.util.List getBinderList()
|
||||
|
||||
public void writeToParcel(android.os.Parcel parcel, int flags) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (binder, Landroid/os/IBinder;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeStrongBinder, (Landroid/os/IBinder;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (binderArray, [Landroid/os/IBinder;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeBinderArray, ([Landroid/os/IBinder;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (binderList, Ljava/util/List;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeBinderList, (Ljava/util/List;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (binderArrayList, Ljava/util/ArrayList;)
|
||||
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
|
||||
ALOAD (1)
|
||||
SWAP
|
||||
INVOKEINTERFACE (java/util/Iterator, next, ()Ljava/lang/Object;)
|
||||
CHECKCAST
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeStrongBinder, (Landroid/os/IBinder;)V)
|
||||
GOTO (L1)
|
||||
LABEL (L2)
|
||||
POP
|
||||
RETURN
|
||||
LABEL (L3)
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final User createFromParcel(android.os.Parcel parcel)
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel source)
|
||||
|
||||
public final User[] newArray(int size)
|
||||
|
||||
public java.lang.Object[] newArray(int size)
|
||||
}
|
||||
|
||||
public final class User : java/lang/Object, android/os/Parcelable {
|
||||
public final static android.os.Parcelable$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>()
|
||||
|
||||
public void <init>(java.lang.String firstName, java.lang.String lastName, int age, boolean isProUser)
|
||||
|
||||
public int describeContents() {
|
||||
LABEL (L0)
|
||||
LINENUMBER (9)
|
||||
BIPUSH (100)
|
||||
IRETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public final int getAge()
|
||||
|
||||
public final java.lang.String getFirstName()
|
||||
|
||||
public final java.lang.String getLastName()
|
||||
|
||||
public final boolean isProUser()
|
||||
|
||||
public void writeToParcel(android.os.Parcel out, int flags)
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// CURIOUS_ABOUT describeContents
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
class User(val firstName: String, val lastName: String, val age: Int, val isProUser: Boolean) : Parcelable {
|
||||
override fun describeContents() = 100
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final User createFromParcel(android.os.Parcel in)
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel p0)
|
||||
|
||||
public final User[] newArray(int size)
|
||||
|
||||
public java.lang.Object[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class User : java/lang/Object, android/os/Parcelable {
|
||||
public final static android.os.Parcelable$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>()
|
||||
|
||||
public void <init>(java.lang.String firstName, java.lang.String lastName, int age, boolean isProUser)
|
||||
|
||||
public int describeContents() {
|
||||
LABEL (L0)
|
||||
LINENUMBER (9)
|
||||
BIPUSH (100)
|
||||
IRETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public final int getAge()
|
||||
|
||||
public final java.lang.String getFirstName()
|
||||
|
||||
public final java.lang.String getLastName()
|
||||
|
||||
public final boolean isProUser()
|
||||
|
||||
public void writeToParcel(android.os.Parcel parcel, int flags)
|
||||
}
|
||||
Vendored
+69
@@ -0,0 +1,69 @@
|
||||
public final class test/Foo$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final test.Foo createFromParcel(android.os.Parcel parcel) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
NEW
|
||||
DUP
|
||||
ALOAD (1)
|
||||
LDC (Ltest/Foo;)
|
||||
INVOKEVIRTUAL (java/lang/Class, getClassLoader, ()Ljava/lang/ClassLoader;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readParcelable, (Ljava/lang/ClassLoader;)Landroid/os/Parcelable;)
|
||||
CHECKCAST
|
||||
INVOKESPECIAL (test/Foo, <init>, (Landroid/accounts/Account;)V)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel source) {
|
||||
LABEL (L0)
|
||||
ALOAD (0)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (test/Foo$Creator, createFromParcel, (Landroid/os/Parcel;)Ltest/Foo;)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public final test.Foo[] newArray(int size)
|
||||
|
||||
public java.lang.Object[] newArray(int size)
|
||||
}
|
||||
|
||||
public final class test/Foo : java/lang/Object, android/os/Parcelable {
|
||||
public final static android.os.Parcelable$Creator CREATOR
|
||||
|
||||
private final android.accounts.Account kp
|
||||
|
||||
static void <clinit>() {
|
||||
NEW
|
||||
DUP
|
||||
INVOKESPECIAL (test/Foo$Creator, <init>, ()V)
|
||||
CHECKCAST
|
||||
PUTSTATIC (CREATOR, Landroid/os/Parcelable$Creator;)
|
||||
RETURN
|
||||
}
|
||||
|
||||
public void <init>(android.accounts.Account kp)
|
||||
|
||||
public int describeContents()
|
||||
|
||||
public final android.accounts.Account getKp()
|
||||
|
||||
public void writeToParcel(android.os.Parcel out, int flags) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (out)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (kp, Landroid/accounts/Account;)
|
||||
CHECKCAST
|
||||
ILOAD (2)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeParcelable, (Landroid/os/Parcelable;I)V)
|
||||
RETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// CURIOUS_ABOUT writeToParcel, createFromParcel, <clinit>
|
||||
// WITH_RUNTIME
|
||||
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.*
|
||||
import android.accounts.Account
|
||||
|
||||
@Parcelize
|
||||
class Foo(val kp: Account): Parcelable
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
public final class test/Foo$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final test.Foo createFromParcel(android.os.Parcel in) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (in)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
NEW
|
||||
DUP
|
||||
ALOAD (1)
|
||||
LDC (Ltest/Foo;)
|
||||
INVOKEVIRTUAL (java/lang/Class, getClassLoader, ()Ljava/lang/ClassLoader;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readParcelable, (Ljava/lang/ClassLoader;)Landroid/os/Parcelable;)
|
||||
CHECKCAST
|
||||
INVOKESPECIAL (test/Foo, <init>, (Landroid/accounts/Account;)V)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel p0) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (11)
|
||||
ALOAD (0)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (test/Foo$Creator, createFromParcel, (Landroid/os/Parcel;)Ltest/Foo;)
|
||||
ARETURN
|
||||
}
|
||||
|
||||
public final test.Foo[] newArray(int size)
|
||||
|
||||
public java.lang.Object[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class test/Foo : java/lang/Object, android/os/Parcelable {
|
||||
public final static android.os.Parcelable$Creator CREATOR
|
||||
|
||||
private final android.accounts.Account kp
|
||||
|
||||
static void <clinit>() {
|
||||
NEW
|
||||
DUP
|
||||
INVOKESPECIAL (test/Foo$Creator, <init>, ()V)
|
||||
PUTSTATIC (CREATOR, Landroid/os/Parcelable$Creator;)
|
||||
RETURN
|
||||
}
|
||||
|
||||
public void <init>(android.accounts.Account kp)
|
||||
|
||||
public int describeContents()
|
||||
|
||||
public final android.accounts.Account getKp()
|
||||
|
||||
public void writeToParcel(android.os.Parcel parcel, int flags) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (kp, Landroid/accounts/Account;)
|
||||
ILOAD (2)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeParcelable, (Landroid/os/Parcelable;I)V)
|
||||
RETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
public final class k/KotlinParcelable$Companion : java/lang/Object {
|
||||
private void <init>()
|
||||
|
||||
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker)
|
||||
}
|
||||
|
||||
public final class k/KotlinParcelable$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public k.KotlinParcelable createFromParcel(android.os.Parcel source) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (source)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
LABEL (L1)
|
||||
LINENUMBER (23)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
|
||||
ISTORE (2)
|
||||
LABEL (L2)
|
||||
LINENUMBER (24)
|
||||
NEW
|
||||
DUP
|
||||
ILOAD (2)
|
||||
INVOKESPECIAL (k/KotlinParcelable, <init>, (I)V)
|
||||
ARETURN
|
||||
LABEL (L3)
|
||||
}
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel p0) {
|
||||
LABEL (L0)
|
||||
ALOAD (0)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (k/KotlinParcelable$Creator, createFromParcel, (Landroid/os/Parcel;)Lk/KotlinParcelable;)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public k.KotlinParcelable[] newArray(int size)
|
||||
|
||||
public java.lang.Object[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class k/KotlinParcelable : java/lang/Object, android/os/Parcelable {
|
||||
public final static k.KotlinParcelable$Creator CREATOR
|
||||
|
||||
public final static k.KotlinParcelable$Companion Companion
|
||||
|
||||
private int data
|
||||
|
||||
static void <clinit>() {
|
||||
NEW
|
||||
DUP
|
||||
ACONST_NULL
|
||||
INVOKESPECIAL (k/KotlinParcelable$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
|
||||
PUTSTATIC (Companion, Lk/KotlinParcelable$Companion;)
|
||||
LABEL (L0)
|
||||
LINENUMBER (18)
|
||||
NEW
|
||||
DUP
|
||||
INVOKESPECIAL (k/KotlinParcelable$Creator, <init>, ()V)
|
||||
PUTSTATIC (CREATOR, Lk/KotlinParcelable$Creator;)
|
||||
RETURN
|
||||
}
|
||||
|
||||
public void <init>(int data)
|
||||
|
||||
public final int component1()
|
||||
|
||||
public final k.KotlinParcelable copy(int data)
|
||||
|
||||
public static k.KotlinParcelable copy$default(k.KotlinParcelable p0, int p1, int p2, java.lang.Object p3)
|
||||
|
||||
public int describeContents()
|
||||
|
||||
public boolean equals(java.lang.Object other)
|
||||
|
||||
public final int getData()
|
||||
|
||||
public int hashCode()
|
||||
|
||||
public final void setData(int <set-?>)
|
||||
|
||||
public java.lang.String toString()
|
||||
|
||||
public void writeToParcel(android.os.Parcel dest, int flags) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (dest)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
LABEL (L1)
|
||||
LINENUMBER (13)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (data, I)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
LABEL (L2)
|
||||
LINENUMBER (14)
|
||||
RETURN
|
||||
LABEL (L3)
|
||||
}
|
||||
}
|
||||
|
||||
public final class test/Foo$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final test.Foo createFromParcel(android.os.Parcel parcel) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
NEW
|
||||
DUP
|
||||
GETSTATIC (CREATOR, Lk/KotlinParcelable$Creator;)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (k/KotlinParcelable$Creator, createFromParcel, (Landroid/os/Parcel;)Lk/KotlinParcelable;)
|
||||
INVOKESPECIAL (test/Foo, <init>, (Lk/KotlinParcelable;)V)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel source) {
|
||||
LABEL (L0)
|
||||
ALOAD (0)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (test/Foo$Creator, createFromParcel, (Landroid/os/Parcel;)Ltest/Foo;)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public final test.Foo[] newArray(int size)
|
||||
|
||||
public java.lang.Object[] newArray(int size)
|
||||
}
|
||||
|
||||
public final class test/Foo : java/lang/Object, android/os/Parcelable {
|
||||
public final static android.os.Parcelable$Creator CREATOR
|
||||
|
||||
private final k.KotlinParcelable kp
|
||||
|
||||
static void <clinit>() {
|
||||
NEW
|
||||
DUP
|
||||
INVOKESPECIAL (test/Foo$Creator, <init>, ()V)
|
||||
CHECKCAST
|
||||
PUTSTATIC (CREATOR, Landroid/os/Parcelable$Creator;)
|
||||
RETURN
|
||||
}
|
||||
|
||||
public void <init>(k.KotlinParcelable kp)
|
||||
|
||||
public int describeContents()
|
||||
|
||||
public final k.KotlinParcelable getKp()
|
||||
|
||||
public void writeToParcel(android.os.Parcel out, int flags) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (out)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (0)
|
||||
GETFIELD (kp, Lk/KotlinParcelable;)
|
||||
ALOAD (1)
|
||||
ILOAD (2)
|
||||
INVOKEVIRTUAL (k/KotlinParcelable, writeToParcel, (Landroid/os/Parcel;I)V)
|
||||
RETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// CURIOUS_ABOUT writeToParcel, createFromParcel, <clinit>
|
||||
// WITH_RUNTIME
|
||||
|
||||
// FILE: KotlinParcelable.kt
|
||||
package k
|
||||
import android.os.*
|
||||
|
||||
data class KotlinParcelable(var data: Int): Parcelable {
|
||||
|
||||
override fun describeContents() = 1
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeInt(data)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val CREATOR = Creator()
|
||||
}
|
||||
|
||||
class Creator : Parcelable.Creator<KotlinParcelable> {
|
||||
override fun createFromParcel(source: Parcel): KotlinParcelable {
|
||||
val data = source.readInt()
|
||||
return KotlinParcelable(data)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int) = arrayOfNulls<KotlinParcelable>(size)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FILE: test.kt
|
||||
package test
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.*
|
||||
import k.*
|
||||
|
||||
@Parcelize
|
||||
class Foo(val kp: KotlinParcelable): Parcelable
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
public final class k/KotlinParcelable$Companion : java/lang/Object {
|
||||
private void <init>()
|
||||
|
||||
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker)
|
||||
}
|
||||
|
||||
public final class k/KotlinParcelable$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public k.KotlinParcelable createFromParcel(android.os.Parcel source) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (source)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
LABEL (L1)
|
||||
LINENUMBER (23)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
|
||||
ISTORE (2)
|
||||
LABEL (L2)
|
||||
LINENUMBER (24)
|
||||
NEW
|
||||
DUP
|
||||
ILOAD (2)
|
||||
INVOKESPECIAL (k/KotlinParcelable, <init>, (I)V)
|
||||
ARETURN
|
||||
LABEL (L3)
|
||||
}
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel p0) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (21)
|
||||
ALOAD (0)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (k/KotlinParcelable$Creator, createFromParcel, (Landroid/os/Parcel;)Lk/KotlinParcelable;)
|
||||
ARETURN
|
||||
}
|
||||
|
||||
public k.KotlinParcelable[] newArray(int size)
|
||||
|
||||
public java.lang.Object[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class k/KotlinParcelable : java/lang/Object, android/os/Parcelable {
|
||||
public final static k.KotlinParcelable$Creator CREATOR
|
||||
|
||||
public final static k.KotlinParcelable$Companion Companion
|
||||
|
||||
private int data
|
||||
|
||||
static void <clinit>() {
|
||||
NEW
|
||||
DUP
|
||||
ACONST_NULL
|
||||
INVOKESPECIAL (k/KotlinParcelable$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
|
||||
PUTSTATIC (Companion, Lk/KotlinParcelable$Companion;)
|
||||
LABEL (L0)
|
||||
LINENUMBER (18)
|
||||
NEW
|
||||
DUP
|
||||
INVOKESPECIAL (k/KotlinParcelable$Creator, <init>, ()V)
|
||||
PUTSTATIC (CREATOR, Lk/KotlinParcelable$Creator;)
|
||||
RETURN
|
||||
}
|
||||
|
||||
public void <init>(int data)
|
||||
|
||||
public final int component1()
|
||||
|
||||
public final k.KotlinParcelable copy(int data)
|
||||
|
||||
public static k.KotlinParcelable copy$default(k.KotlinParcelable p0, int p1, int p2, java.lang.Object p3)
|
||||
|
||||
public int describeContents()
|
||||
|
||||
public boolean equals(java.lang.Object p0)
|
||||
|
||||
public final int getData()
|
||||
|
||||
public int hashCode()
|
||||
|
||||
public final void setData(int <set-?>)
|
||||
|
||||
public java.lang.String toString()
|
||||
|
||||
public void writeToParcel(android.os.Parcel dest, int flags) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (dest)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
LABEL (L1)
|
||||
LINENUMBER (13)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (data, I)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
LABEL (L2)
|
||||
LINENUMBER (14)
|
||||
RETURN
|
||||
LABEL (L3)
|
||||
}
|
||||
}
|
||||
|
||||
public final class test/Foo$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final test.Foo createFromParcel(android.os.Parcel in) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (in)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
NEW
|
||||
DUP
|
||||
ALOAD (1)
|
||||
LDC (Ltest/Foo;)
|
||||
INVOKEVIRTUAL (java/lang/Class, getClassLoader, ()Ljava/lang/ClassLoader;)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readParcelable, (Ljava/lang/ClassLoader;)Landroid/os/Parcelable;)
|
||||
CHECKCAST
|
||||
INVOKESPECIAL (test/Foo, <init>, (Lk/KotlinParcelable;)V)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel p0) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (9)
|
||||
ALOAD (0)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (test/Foo$Creator, createFromParcel, (Landroid/os/Parcel;)Ltest/Foo;)
|
||||
ARETURN
|
||||
}
|
||||
|
||||
public final test.Foo[] newArray(int size)
|
||||
|
||||
public java.lang.Object[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class test/Foo : java/lang/Object, android/os/Parcelable {
|
||||
public final static android.os.Parcelable$Creator CREATOR
|
||||
|
||||
private final k.KotlinParcelable kp
|
||||
|
||||
static void <clinit>() {
|
||||
NEW
|
||||
DUP
|
||||
INVOKESPECIAL (test/Foo$Creator, <init>, ()V)
|
||||
PUTSTATIC (CREATOR, Landroid/os/Parcelable$Creator;)
|
||||
RETURN
|
||||
}
|
||||
|
||||
public void <init>(k.KotlinParcelable kp)
|
||||
|
||||
public int describeContents()
|
||||
|
||||
public final k.KotlinParcelable getKp()
|
||||
|
||||
public void writeToParcel(android.os.Parcel parcel, int flags) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (kp, Lk/KotlinParcelable;)
|
||||
ILOAD (2)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeParcelable, (Landroid/os/Parcelable;I)V)
|
||||
RETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
final class User$Companion : java/lang/Object, kotlinx/parcelize/Parceler {
|
||||
private void <init>()
|
||||
|
||||
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker)
|
||||
|
||||
public User create(android.os.Parcel parcel)
|
||||
|
||||
public java.lang.Object create(android.os.Parcel parcel)
|
||||
|
||||
public User[] newArray(int size) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (10)
|
||||
ALOAD (0)
|
||||
ILOAD (1)
|
||||
INVOKESTATIC (kotlinx/parcelize/Parceler$DefaultImpls, newArray, (Lkotlinx/parcelize/Parceler;I)[Ljava/lang/Object;)
|
||||
CHECKCAST
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object[] newArray(int size) {
|
||||
LABEL (L0)
|
||||
ALOAD (0)
|
||||
ILOAD (1)
|
||||
INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public void write(User $this$write, android.os.Parcel parcel, int flags)
|
||||
|
||||
public void write(java.lang.Object $this$write, android.os.Parcel parcel, int flags)
|
||||
}
|
||||
|
||||
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final User createFromParcel(android.os.Parcel parcel) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
INVOKESTATIC (User, access$getCompanion$p$s2645995, ()LUser$Companion;)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (User$Companion, create, (Landroid/os/Parcel;)LUser;)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel source) {
|
||||
LABEL (L0)
|
||||
ALOAD (0)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (User$Creator, createFromParcel, (Landroid/os/Parcel;)LUser;)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public final User[] newArray(int size) {
|
||||
LABEL (L0)
|
||||
ILOAD (1)
|
||||
ANEWARRAY
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object[] newArray(int size) {
|
||||
LABEL (L0)
|
||||
ALOAD (0)
|
||||
ILOAD (1)
|
||||
INVOKEVIRTUAL (User$Creator, newArray, (I)[LUser;)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
|
||||
public final class User : java/lang/Object, android/os/Parcelable {
|
||||
public final static android.os.Parcelable$Creator CREATOR
|
||||
|
||||
private final static User$Companion Companion
|
||||
|
||||
private final int age
|
||||
|
||||
private final java.lang.String firstName
|
||||
|
||||
private final java.lang.String lastName
|
||||
|
||||
static void <clinit>()
|
||||
|
||||
public void <init>(java.lang.String firstName, java.lang.String lastName, int age)
|
||||
|
||||
public final static User$Companion access$getCompanion$p$s2645995()
|
||||
|
||||
public int describeContents()
|
||||
|
||||
public final int getAge()
|
||||
|
||||
public final java.lang.String getFirstName()
|
||||
|
||||
public final java.lang.String getLastName()
|
||||
|
||||
public void writeToParcel(android.os.Parcel out, int flags) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (out)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
GETSTATIC (Companion, LUser$Companion;)
|
||||
ALOAD (0)
|
||||
ALOAD (1)
|
||||
ILOAD (2)
|
||||
INVOKEVIRTUAL (User$Companion, write, (LUser;Landroid/os/Parcel;I)V)
|
||||
RETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// CURIOUS_ABOUT writeToParcel, createFromParcel, newArray
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
class User(val firstName: String, val lastName: String, val age: Int) : Parcelable {
|
||||
private companion object : Parceler<User> {
|
||||
override fun User.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(firstName)
|
||||
parcel.writeString(lastName)
|
||||
parcel.writeInt(age)
|
||||
}
|
||||
|
||||
override fun create(parcel: Parcel) = User(parcel.readString(), parcel.readString(), parcel.readInt())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
final class User$Companion : java/lang/Object, kotlinx/parcelize/Parceler {
|
||||
private void <init>()
|
||||
|
||||
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker)
|
||||
|
||||
public User create(android.os.Parcel parcel)
|
||||
|
||||
public java.lang.Object create(android.os.Parcel p0)
|
||||
|
||||
public User[] newArray(int size) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (10)
|
||||
ALOAD (0)
|
||||
ILOAD (1)
|
||||
INVOKESTATIC (kotlinx/parcelize/Parceler$DefaultImpls, newArray, (Lkotlinx/parcelize/Parceler;I)[Ljava/lang/Object;)
|
||||
CHECKCAST
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object[] newArray(int p0) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (10)
|
||||
ALOAD (0)
|
||||
ILOAD (1)
|
||||
INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;)
|
||||
ARETURN
|
||||
}
|
||||
|
||||
public void write(User $this$write, android.os.Parcel parcel, int flags)
|
||||
|
||||
public void write(java.lang.Object p0, android.os.Parcel p1, int p2)
|
||||
}
|
||||
|
||||
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final User createFromParcel(android.os.Parcel in) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (in)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
GETSTATIC (Companion, LUser$Companion;)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (User$Companion, create, (Landroid/os/Parcel;)LUser;)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel p0) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (9)
|
||||
ALOAD (0)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (User$Creator, createFromParcel, (Landroid/os/Parcel;)LUser;)
|
||||
ARETURN
|
||||
}
|
||||
|
||||
public final User[] newArray(int size) {
|
||||
LABEL (L0)
|
||||
ILOAD (1)
|
||||
ANEWARRAY
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object[] newArray(int p0) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (9)
|
||||
ALOAD (0)
|
||||
ILOAD (1)
|
||||
INVOKEVIRTUAL (User$Creator, newArray, (I)[LUser;)
|
||||
ARETURN
|
||||
}
|
||||
}
|
||||
|
||||
public final class User : java/lang/Object, android/os/Parcelable {
|
||||
public final static android.os.Parcelable$Creator CREATOR
|
||||
|
||||
private final static User$Companion Companion
|
||||
|
||||
private final int age
|
||||
|
||||
private final java.lang.String firstName
|
||||
|
||||
private final java.lang.String lastName
|
||||
|
||||
static void <clinit>()
|
||||
|
||||
public void <init>(java.lang.String firstName, java.lang.String lastName, int age)
|
||||
|
||||
public int describeContents()
|
||||
|
||||
public final int getAge()
|
||||
|
||||
public final java.lang.String getFirstName()
|
||||
|
||||
public final java.lang.String getLastName()
|
||||
|
||||
public void writeToParcel(android.os.Parcel parcel, int flags) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
GETSTATIC (Companion, LUser$Companion;)
|
||||
ALOAD (0)
|
||||
ALOAD (1)
|
||||
ILOAD (2)
|
||||
INVOKEVIRTUAL (User$Companion, write, (LUser;Landroid/os/Parcel;I)V)
|
||||
RETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
final class User$Companion : java/lang/Object, kotlinx/parcelize/Parceler {
|
||||
private void <init>()
|
||||
|
||||
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker)
|
||||
|
||||
public User create(android.os.Parcel parcel)
|
||||
|
||||
public java.lang.Object create(android.os.Parcel parcel)
|
||||
|
||||
public User[] newArray(int size) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (19)
|
||||
ILOAD (1)
|
||||
ANEWARRAY
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object[] newArray(int size) {
|
||||
LABEL (L0)
|
||||
ALOAD (0)
|
||||
ILOAD (1)
|
||||
INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public void write(User $this$write, android.os.Parcel parcel, int flags)
|
||||
|
||||
public void write(java.lang.Object $this$write, android.os.Parcel parcel, int flags)
|
||||
}
|
||||
|
||||
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final User createFromParcel(android.os.Parcel parcel)
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel source)
|
||||
|
||||
public final User[] newArray(int size) {
|
||||
LABEL (L0)
|
||||
INVOKESTATIC (User, access$getCompanion$p$s2645995, ()LUser$Companion;)
|
||||
ILOAD (1)
|
||||
INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object[] newArray(int size) {
|
||||
LABEL (L0)
|
||||
ALOAD (0)
|
||||
ILOAD (1)
|
||||
INVOKEVIRTUAL (User$Creator, newArray, (I)[LUser;)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
|
||||
public final class User : java/lang/Object, android/os/Parcelable {
|
||||
public final static android.os.Parcelable$Creator CREATOR
|
||||
|
||||
private final static User$Companion Companion
|
||||
|
||||
private final int age
|
||||
|
||||
private final java.lang.String firstName
|
||||
|
||||
private final java.lang.String lastName
|
||||
|
||||
static void <clinit>()
|
||||
|
||||
public void <init>(java.lang.String firstName, java.lang.String lastName, int age)
|
||||
|
||||
public final static User$Companion access$getCompanion$p$s2645995()
|
||||
|
||||
public int describeContents()
|
||||
|
||||
public final int getAge()
|
||||
|
||||
public final java.lang.String getFirstName()
|
||||
|
||||
public final java.lang.String getLastName()
|
||||
|
||||
public void writeToParcel(android.os.Parcel out, int flags)
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// CURIOUS_ABOUT newArray
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
class User(val firstName: String, val lastName: String, val age: Int) : Parcelable {
|
||||
private companion object : Parceler<User> {
|
||||
override fun User.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(firstName)
|
||||
parcel.writeString(lastName)
|
||||
parcel.writeInt(age)
|
||||
}
|
||||
|
||||
override fun create(parcel: Parcel) = User(parcel.readString(), parcel.readString(), parcel.readInt())
|
||||
|
||||
override fun newArray(size: Int) = arrayOfNulls<User>(size) as Array<User>
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
final class User$Companion : java/lang/Object, kotlinx/parcelize/Parceler {
|
||||
private void <init>()
|
||||
|
||||
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker)
|
||||
|
||||
public User create(android.os.Parcel parcel)
|
||||
|
||||
public java.lang.Object create(android.os.Parcel p0)
|
||||
|
||||
public User[] newArray(int size) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (19)
|
||||
ILOAD (1)
|
||||
ANEWARRAY
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object[] newArray(int p0) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (10)
|
||||
ALOAD (0)
|
||||
ILOAD (1)
|
||||
INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;)
|
||||
ARETURN
|
||||
}
|
||||
|
||||
public void write(User $this$write, android.os.Parcel parcel, int flags)
|
||||
|
||||
public void write(java.lang.Object p0, android.os.Parcel p1, int p2)
|
||||
}
|
||||
|
||||
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final User createFromParcel(android.os.Parcel in)
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel p0)
|
||||
|
||||
public final User[] newArray(int size) {
|
||||
LABEL (L0)
|
||||
GETSTATIC (Companion, LUser$Companion;)
|
||||
ILOAD (1)
|
||||
INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;)
|
||||
ARETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public java.lang.Object[] newArray(int p0) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (9)
|
||||
ALOAD (0)
|
||||
ILOAD (1)
|
||||
INVOKEVIRTUAL (User$Creator, newArray, (I)[LUser;)
|
||||
ARETURN
|
||||
}
|
||||
}
|
||||
|
||||
public final class User : java/lang/Object, android/os/Parcelable {
|
||||
public final static android.os.Parcelable$Creator CREATOR
|
||||
|
||||
private final static User$Companion Companion
|
||||
|
||||
private final int age
|
||||
|
||||
private final java.lang.String firstName
|
||||
|
||||
private final java.lang.String lastName
|
||||
|
||||
static void <clinit>()
|
||||
|
||||
public void <init>(java.lang.String firstName, java.lang.String lastName, int age)
|
||||
|
||||
public int describeContents()
|
||||
|
||||
public final int getAge()
|
||||
|
||||
public final java.lang.String getFirstName()
|
||||
|
||||
public final java.lang.String getLastName()
|
||||
|
||||
public void writeToParcel(android.os.Parcel parcel, int flags)
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
public abstract class AbstractUser : java/lang/Object, android/os/Parcelable {
|
||||
public void <init>()
|
||||
|
||||
public int describeContents() {
|
||||
LABEL (L0)
|
||||
LINENUMBER (8)
|
||||
BIPUSH (100)
|
||||
IRETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
|
||||
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final User createFromParcel(android.os.Parcel parcel)
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel source)
|
||||
|
||||
public final User[] newArray(int size)
|
||||
|
||||
public java.lang.Object[] newArray(int size)
|
||||
}
|
||||
|
||||
public final class User : AbstractUser {
|
||||
public final static android.os.Parcelable$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>()
|
||||
|
||||
public void <init>(java.lang.String firstName, java.lang.String lastName, int age, boolean isProUser)
|
||||
|
||||
public final int getAge()
|
||||
|
||||
public final java.lang.String getFirstName()
|
||||
|
||||
public final java.lang.String getLastName()
|
||||
|
||||
public final boolean isProUser()
|
||||
|
||||
public void writeToParcel(android.os.Parcel out, int flags)
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// CURIOUS_ABOUT describeContents
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlinx.parcelize.*
|
||||
import android.os.Parcelable
|
||||
|
||||
abstract class AbstractUser : Parcelable {
|
||||
override fun describeContents() = 100
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
class User(val firstName: String, val lastName: String, val age: Int, val isProUser: Boolean) : AbstractUser()
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
public abstract class AbstractUser : java/lang/Object, android/os/Parcelable {
|
||||
public void <init>()
|
||||
|
||||
public int describeContents() {
|
||||
LABEL (L0)
|
||||
LINENUMBER (8)
|
||||
BIPUSH (100)
|
||||
IRETURN
|
||||
LABEL (L1)
|
||||
}
|
||||
}
|
||||
|
||||
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final User createFromParcel(android.os.Parcel in)
|
||||
|
||||
public java.lang.Object createFromParcel(android.os.Parcel p0)
|
||||
|
||||
public final User[] newArray(int size)
|
||||
|
||||
public java.lang.Object[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class User : AbstractUser {
|
||||
public final static android.os.Parcelable$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>()
|
||||
|
||||
public void <init>(java.lang.String firstName, java.lang.String lastName, int age, boolean isProUser)
|
||||
|
||||
public final int getAge()
|
||||
|
||||
public final java.lang.String getFirstName()
|
||||
|
||||
public final java.lang.String getLastName()
|
||||
|
||||
public final boolean isProUser()
|
||||
|
||||
public void writeToParcel(android.os.Parcel parcel, int flags)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user