diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/UltraLightClassCodegenSupport.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/UltraLightClassCodegenSupport.kt index 2e8b74f2202..0655ec862f6 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/UltraLightClassCodegenSupport.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/UltraLightClassCodegenSupport.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.asJava import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass +import org.jetbrains.kotlin.asJava.elements.KtLightField import org.jetbrains.kotlin.asJava.elements.KtLightMethod import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.psi.KtDeclaration @@ -17,4 +18,11 @@ interface UltraLightClassCodegenSupport { containingDeclaration: KtUltraLightClass, methodsList: MutableList ) = Unit + + fun interceptFieldsBuilding( + declaration: KtDeclaration, + descriptor: Lazy, + containingDeclaration: KtUltraLightClass, + methodsList: MutableList + ) = Unit } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt index f977485000b..13e6ebb22de 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt @@ -75,6 +75,8 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor private val _deprecated by lazyPub { classOrObject.isDeprecated(support) } + private val descriptor = lazyPub { getDescriptor() } + override fun isFinal(isFinalByPsi: Boolean) = if (tooComplex) super.isFinal(isFinalByPsi) else isFinalByPsi @Volatile @@ -216,7 +218,18 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor } } - if (isAnnotationType) return@lazyPub result + fun ArrayList.updateWithCompilerPlugins() = also { + applyCompilerPlugins { + it.interceptFieldsBuilding( + declaration = kotlinOrigin, + descriptor = descriptor, + containingDeclaration = this@KtUltraLightClass, + fieldsList = result + ) + } + } + + if (isAnnotationType) return@lazyPub result.updateWithCompilerPlugins() for (parameter in propertyParameters()) { membersBuilder.createPropertyField(parameter, usedNames, forceStatic = false)?.let(result::add) @@ -259,7 +272,7 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor } } - result + result.updateWithCompilerPlugins() } private fun isNamedObject() = classOrObject is KtObjectDeclaration && !classOrObject.isCompanion() @@ -317,17 +330,24 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor addMethodsFromDataClass(result) addDelegatesToInterfaceMethods(result) - val lazyDescriptor = lazy { getDescriptor() } - - ExpressionCodegenExtension.getInstances(project).forEach { - if (it is UltraLightClassCodegenSupport) { - it.interceptMethodsBuilding(kotlinOrigin, lazyDescriptor, this, result) - } + applyCompilerPlugins { + it.interceptMethodsBuilding( + declaration = kotlinOrigin, + descriptor = descriptor, + containingDeclaration = this, + methodsList = result + ) } return result } + private inline fun applyCompilerPlugins(body: (UltraLightClassCodegenSupport) -> Unit) { + ExpressionCodegenExtension.getInstances(project) + .filterIsInstance() + .forEach { body(it) } + } + private val _ownMethods: CachedValue> = CachedValuesManager.getManager(project).createCachedValue( { CachedValueProvider.Result.create( diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableCodegenExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableCodegenExtension.kt index 84539917e64..a823843cfb0 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableCodegenExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableCodegenExtension.kt @@ -1,20 +1,9 @@ +/* + * 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.android.parcel -/* - * 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. - */ import kotlinx.android.parcel.TypeParceler import org.jetbrains.kotlin.android.parcel.ParcelableResolveExtension.Companion.createMethod @@ -71,14 +60,16 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension { ) protected open fun isExperimental(element: KtElement) = true + protected val ClassDescriptor.isParcelableClassDescriptor get() = kind in ALLOWED_CLASS_KINDS && isParcelize + override val shouldGenerateClassSyntheticPartsInLightClassesMode: Boolean get() = true override fun generateClassSyntheticParts(codegen: ImplementationBodyCodegen) { - val parcelableClass = codegen.descriptor - if (!parcelableClass.isParcelize) return - if (parcelableClass.kind !in ALLOWED_CLASS_KINDS) return + val parcelableClass = codegen.descriptor + + if (!parcelableClass.isParcelableClassDescriptor) return val propertiesToSerialize = getPropertiesToSerialize(codegen, parcelableClass) @@ -108,7 +99,7 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension { } } - private fun ClassDescriptor.hasCreatorField(): Boolean { + protected fun ClassDescriptor.hasCreatorField(): Boolean { val companionObject = companionObjectDescriptor ?: return false if (companionObject.name == CREATOR_NAME) { @@ -120,11 +111,11 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension { .isNotEmpty() } - private fun ClassDescriptor.hasSyntheticDescribeContents() = hasParcelizeSyntheticMethod(ComponentKind.DESCRIBE_CONTENTS) + protected fun ClassDescriptor.hasSyntheticDescribeContents() = hasParcelizeSyntheticMethod(ComponentKind.DESCRIBE_CONTENTS) - private fun ClassDescriptor.hasSyntheticWriteToParcel() = hasParcelizeSyntheticMethod(ComponentKind.WRITE_TO_PARCEL) + protected fun ClassDescriptor.hasSyntheticWriteToParcel() = hasParcelizeSyntheticMethod(ComponentKind.WRITE_TO_PARCEL) - private fun ClassDescriptor.hasParcelizeSyntheticMethod(componentKind: ParcelableSyntheticComponent.ComponentKind): Boolean { + protected fun ClassDescriptor.hasParcelizeSyntheticMethod(componentKind: ParcelableSyntheticComponent.ComponentKind): Boolean { val methodName = Name.identifier(componentKind.methodName) val writeToParcelMethods = unsubstitutedMemberScope @@ -421,7 +412,7 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension { }) } - private fun ClassDescriptor.findFunction(componentKind: ParcelableSyntheticComponent.ComponentKind): SimpleFunctionDescriptor? { + protected fun ClassDescriptor.findFunction(componentKind: ParcelableSyntheticComponent.ComponentKind): SimpleFunctionDescriptor? { return unsubstitutedMemberScope .getContributedFunctions(Name.identifier(componentKind.methodName), WHEN_GET_ALL_DESCRIPTORS) .firstOrNull { (it as? ParcelableSyntheticComponent)?.componentKind == componentKind } diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/IDEParcelableApplicabilityExtension.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/IDEParcelableApplicabilityExtension.kt index afb9ba75bfa..a2ef9c0a619 100644 --- a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/IDEParcelableApplicabilityExtension.kt +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/IDEParcelableApplicabilityExtension.kt @@ -19,14 +19,15 @@ class IDEParcelableApplicabilityExtension : LightClassApplicabilityCheckExtensio override fun checkApplicabilityType(declaration: KtDeclaration, descriptor: Lazy): LightClassApplicabilityType { - if (!declaration.isOrdinaryClass || !declaration.isAnnotated) return UltraLightClass - - val descriptorValue = descriptor.value ?: return UltraLightClass - - val classDescriptor = (descriptorValue as? ClassDescriptor) - ?: descriptorValue.containingDeclaration as? ClassDescriptor - ?: return UltraLightClass - - return if (classDescriptor.isParcelize) LightClass else UltraLightClass + return UltraLightClass +// if (!declaration.isOrdinaryClass || !declaration.isAnnotated) return UltraLightClass +// +// val descriptorValue = descriptor.value ?: return UltraLightClass +// +// val classDescriptor = (descriptorValue as? ClassDescriptor) +// ?: descriptorValue.containingDeclaration as? ClassDescriptor +// ?: return UltraLightClass +// +// return if (classDescriptor.isParcelize) LightClass else UltraLightClass } } \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/IDEParcelableCodegenExtension.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/IDEParcelableCodegenExtension.kt index b5f87f36549..8c0fc7d0faf 100644 --- a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/IDEParcelableCodegenExtension.kt +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/IDEParcelableCodegenExtension.kt @@ -1,28 +1,104 @@ /* - * 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. + * 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.android.parcel +import com.intellij.psi.impl.light.LightFieldBuilder import org.jetbrains.kotlin.android.synthetic.idea.androidExtensionsIsExperimental +import org.jetbrains.kotlin.asJava.UltraLightClassCodegenSupport +import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass +import org.jetbrains.kotlin.asJava.classes.createGeneratedMethodFromDescriptor +import org.jetbrains.kotlin.asJava.elements.* +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.idea.caches.project.getModuleInfo +import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.util.isAnnotated +import org.jetbrains.kotlin.util.isOrdinaryClass -class IDEParcelableCodegenExtension : ParcelableCodegenExtension() { +class IDEParcelableCodegenExtension : ParcelableCodegenExtension(), UltraLightClassCodegenSupport { override fun isExperimental(element: KtElement): Boolean { val moduleInfo = element.getModuleInfo() return moduleInfo.androidExtensionsIsExperimental } + + private fun tryGetParcelableClass( + declaration: KtDeclaration, + descriptor: Lazy + ): ClassDescriptor? { + + if (!declaration.isOrdinaryClass || !declaration.isAnnotated) return null + + val descriptorValue = descriptor.value ?: return null + + val parcelableClass = (descriptorValue as? ClassDescriptor) + ?: descriptorValue.containingDeclaration as? ClassDescriptor + ?: return null + + if (!parcelableClass.isParcelableClassDescriptor) return null + + return parcelableClass + } + + override fun interceptFieldsBuilding( + declaration: KtDeclaration, + descriptor: Lazy, + containingDeclaration: KtUltraLightClass, + fieldsList: MutableList + ) { + + val parcelableClass = tryGetParcelableClass( + declaration = declaration, + descriptor = descriptor + ) ?: return + + if (parcelableClass.hasCreatorField()) return + + val fieldWrapper = KtLightFieldImpl.KtLightFieldForSourceDeclaration( + origin = null, + computeDelegate = { + LightFieldBuilder("CREATOR", "android.os.Parcelable.Creator", containingDeclaration).also { + it.setModifiers("public", "static", "final") + } + }, + containingClass = containingDeclaration, + dummyDelegate = null + ) + + fieldsList.add(fieldWrapper) + } + + override fun interceptMethodsBuilding( + declaration: KtDeclaration, + descriptor: Lazy, + containingDeclaration: KtUltraLightClass, + methodsList: MutableList + ) { + + val parcelableClass = tryGetParcelableClass( + declaration = declaration, + descriptor = descriptor + ) ?: return + + with(parcelableClass) { + if (hasSyntheticDescribeContents()) { + findFunction(ParcelableSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS)?.let { + methodsList.add( + containingDeclaration.createGeneratedMethodFromDescriptor(it) + ) + } + } + + if (hasSyntheticWriteToParcel()) { + findFunction(ParcelableSyntheticComponent.ComponentKind.WRITE_TO_PARCEL)?.let { + methodsList.add( + containingDeclaration.createGeneratedMethodFromDescriptor(it) + ) + } + } + } + } } \ No newline at end of file