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>kapt</w>
|
||||||
<w>kotlinc</w>
|
<w>kotlinc</w>
|
||||||
<w>mutators</w>
|
<w>mutators</w>
|
||||||
|
<w>parcelable</w>
|
||||||
<w>parceler</w>
|
<w>parceler</w>
|
||||||
|
<w>parcelers</w>
|
||||||
|
<w>parcelize</w>
|
||||||
<w>repl</w>
|
<w>repl</w>
|
||||||
<w>testdata</w>
|
<w>testdata</w>
|
||||||
<w>uast</w>
|
<w>uast</w>
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ dependencies {
|
|||||||
testCompile(projectTests(":plugins:jvm-abi-gen"))
|
testCompile(projectTests(":plugins:jvm-abi-gen"))
|
||||||
testCompile(projectTests(":plugins:android-extensions-compiler"))
|
testCompile(projectTests(":plugins:android-extensions-compiler"))
|
||||||
testCompile(projectTests(":plugins:android-extensions-ide"))
|
testCompile(projectTests(":plugins:android-extensions-ide"))
|
||||||
|
testCompile(projectTests(":plugins:parcelize:parcelize-compiler"))
|
||||||
testCompile(projectTests(":kotlin-annotation-processing"))
|
testCompile(projectTests(":kotlin-annotation-processing"))
|
||||||
testCompile(projectTests(":kotlin-annotation-processing-cli"))
|
testCompile(projectTests(":kotlin-annotation-processing-cli"))
|
||||||
testCompile(projectTests(":kotlin-allopen-compiler-plugin"))
|
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.nj2k.inference.nullability.AbstractNullabilityInferenceTest
|
||||||
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
|
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
|
||||||
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
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.psi.patternMatching.AbstractPsiUnifierTest
|
||||||
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
|
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
|
||||||
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverTest
|
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") {
|
testGroup("plugins/jvm-abi-gen/test", "plugins/jvm-abi-gen/testData") {
|
||||||
testClass<AbstractCompareJvmAbiTest> {
|
testClass<AbstractCompareJvmAbiTest> {
|
||||||
model("compare", recursive = false, extension = null)
|
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.nj2k.inference.nullability.AbstractNullabilityInferenceTest
|
||||||
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
|
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
|
||||||
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
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.psi.patternMatching.AbstractPsiUnifierTest
|
||||||
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
|
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
|
||||||
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverTest
|
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") {
|
testGroup("plugins/kapt3/kapt3-compiler/test", "plugins/kapt3/kapt3-compiler/testData") {
|
||||||
testClass<AbstractClassFileToSourceStubConverterTest> {
|
testClass<AbstractClassFileToSourceStubConverterTest> {
|
||||||
model("converter")
|
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.nj2k.inference.nullability.AbstractNullabilityInferenceTest
|
||||||
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
|
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
|
||||||
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
|
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.psi.patternMatching.AbstractPsiUnifierTest
|
||||||
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
|
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
|
||||||
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverTest
|
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") {
|
testGroup("plugins/kapt3/kapt3-compiler/test", "plugins/kapt3/kapt3-compiler/testData") {
|
||||||
testClass<AbstractClassFileToSourceStubConverterTest> {
|
testClass<AbstractClassFileToSourceStubConverterTest> {
|
||||||
model("converter")
|
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