Clear cache on fragment onDestroy()
This commit is contained in:
+1
-1
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
|||||||
|
|
||||||
public interface ClassBuilderInterceptorExtension {
|
public interface ClassBuilderInterceptorExtension {
|
||||||
companion object : ProjectExtensionDescriptor<ClassBuilderInterceptorExtension>(
|
companion object : ProjectExtensionDescriptor<ClassBuilderInterceptorExtension>(
|
||||||
"org.jetbrains.kotlin.classBuilderFactoryInterceptorExtension", javaClass<ClassBuilderInterceptorExtension>())
|
"org.jetbrains.kotlin.classBuilderFactoryInterceptorExtension", ClassBuilderInterceptorExtension::class.java)
|
||||||
|
|
||||||
public fun interceptClassBuilderFactory(
|
public fun interceptClassBuilderFactory(
|
||||||
interceptedFactory: ClassBuilderFactory,
|
interceptedFactory: ClassBuilderFactory,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<extensionPoint name="simpleNameReferenceExtension"
|
<extensionPoint name="simpleNameReferenceExtension"
|
||||||
interface="org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension"
|
interface="org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension"
|
||||||
area="IDEA_PROJECT"/>
|
area="IDEA_PROJECT"/>
|
||||||
<extensionPoint name="classBuilderInterceptorExtension"
|
<extensionPoint name="classBuilderFactoryInterceptorExtension"
|
||||||
interface="org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension"
|
interface="org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension"
|
||||||
area="IDEA_PROJECT"/>
|
area="IDEA_PROJECT"/>
|
||||||
<extensionPoint name="quickFixContributor"
|
<extensionPoint name="quickFixContributor"
|
||||||
|
|||||||
+3
@@ -21,11 +21,13 @@ import com.intellij.openapi.components.ServiceManager
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension
|
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension
|
||||||
import org.jetbrains.kotlin.android.synthetic.diagnostic.AndroidExtensionPropertiesCallChecker
|
import org.jetbrains.kotlin.android.synthetic.diagnostic.AndroidExtensionPropertiesCallChecker
|
||||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
|
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
|
||||||
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidLayoutXmlFileManager
|
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidLayoutXmlFileManager
|
||||||
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
|
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
|
||||||
import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator
|
import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator
|
||||||
|
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||||
import org.jetbrains.kotlin.compiler.plugin.CliOption
|
import org.jetbrains.kotlin.compiler.plugin.CliOption
|
||||||
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
|
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
|
||||||
@@ -93,6 +95,7 @@ public class AndroidComponentRegistrar : ComponentRegistrar {
|
|||||||
ExternalDeclarationsProvider.registerExtension(project, CliAndroidDeclarationsProvider(project))
|
ExternalDeclarationsProvider.registerExtension(project, CliAndroidDeclarationsProvider(project))
|
||||||
ExpressionCodegenExtension.registerExtension(project, AndroidExpressionCodegenExtension())
|
ExpressionCodegenExtension.registerExtension(project, AndroidExpressionCodegenExtension())
|
||||||
StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor())
|
StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor())
|
||||||
|
ClassBuilderInterceptorExtension.registerExtension(project, AndroidOnDestroyClassBuilderInterceptorExtension())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+96
-65
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.psi.JetClassOrObject
|
|||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
|
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||||
@@ -39,25 +40,57 @@ import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PUBLIC
|
|||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
|
|
||||||
private enum class AndroidClassType(val internalClassName: String, val supportsCache: Boolean = false) {
|
public enum class AndroidClassType(className: String, val supportsCache: Boolean = false, val fragment: Boolean = false) {
|
||||||
ACTIVITY(AndroidConst.ACTIVITY_FQNAME.innerName, true),
|
ACTIVITY(AndroidConst.ACTIVITY_FQNAME, supportsCache = true),
|
||||||
FRAGMENT(AndroidConst.FRAGMENT_FQNAME.innerName, true),
|
FRAGMENT(AndroidConst.FRAGMENT_FQNAME, supportsCache = true, fragment = true),
|
||||||
SUPPORT_FRAGMENT_ACTIVITY(AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME.innerName, true),
|
SUPPORT_FRAGMENT_ACTIVITY(AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME, supportsCache = true),
|
||||||
SUPPORT_FRAGMENT(AndroidConst.SUPPORT_FRAGMENT_FQNAME.innerName, true),
|
SUPPORT_FRAGMENT(AndroidConst.SUPPORT_FRAGMENT_FQNAME, supportsCache = true, fragment = true),
|
||||||
VIEW(AndroidConst.VIEW_FQNAME.innerName),
|
VIEW(AndroidConst.VIEW_FQNAME),
|
||||||
UNKNOWN("")
|
UNKNOWN("");
|
||||||
}
|
|
||||||
|
|
||||||
private val String.innerName: String
|
public val internalClassName: String = className.replace('.', '/')
|
||||||
get() = replace('.', '/')
|
|
||||||
|
public companion object {
|
||||||
|
public fun getClassType(descriptor: ClassifierDescriptor): AndroidClassType {
|
||||||
|
fun getClassTypeInternal(name: String): AndroidClassType? = when (name) {
|
||||||
|
AndroidConst.ACTIVITY_FQNAME -> AndroidClassType.ACTIVITY
|
||||||
|
AndroidConst.FRAGMENT_FQNAME -> AndroidClassType.FRAGMENT
|
||||||
|
AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY
|
||||||
|
AndroidConst.SUPPORT_FRAGMENT_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT
|
||||||
|
AndroidConst.VIEW_FQNAME -> AndroidClassType.VIEW
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptor is LazyJavaClassDescriptor) {
|
||||||
|
val androidClassType = getClassTypeInternal(DescriptorUtils.getFqName(descriptor).asString())
|
||||||
|
if (androidClassType != null) return androidClassType
|
||||||
|
}
|
||||||
|
else if (descriptor is LazyClassDescriptor) { // For tests (FakeActivity)
|
||||||
|
val androidClassType = getClassTypeInternal(DescriptorUtils.getFqName(descriptor).toString())
|
||||||
|
if (androidClassType != null) return androidClassType
|
||||||
|
}
|
||||||
|
|
||||||
|
for (supertype in descriptor.typeConstructor.supertypes) {
|
||||||
|
val declarationDescriptor = supertype.constructor.declarationDescriptor
|
||||||
|
if (declarationDescriptor != null) {
|
||||||
|
val androidClassType = getClassType(declarationDescriptor)
|
||||||
|
if (androidClassType != AndroidClassType.UNKNOWN) return androidClassType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AndroidClassType.UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||||
companion object {
|
companion object {
|
||||||
private val PROPERTY_NAME = "_\$_findViewCache"
|
private val PROPERTY_NAME = "_\$_findViewCache"
|
||||||
private val CACHED_FIND_VIEW_BY_ID_METHOD_NAME = "_\$_findCachedViewById"
|
private val CACHED_FIND_VIEW_BY_ID_METHOD_NAME = "_\$_findCachedViewById"
|
||||||
private val CLEAR_CACHE_METHOD_NAME = "_\$_clearFindViewByIdCache"
|
val CLEAR_CACHE_METHOD_NAME = "_\$_clearFindViewByIdCache"
|
||||||
|
val ON_DESTROY_METHOD_NAME = "onDestroy"
|
||||||
|
|
||||||
fun isCacheSupported(descriptor: ClassifierDescriptor) = descriptor.getSource() is KotlinSourceElement
|
fun isCacheSupported(descriptor: ClassifierDescriptor) = descriptor.source is KotlinSourceElement
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SyntheticPartsGenerateContext(
|
private class SyntheticPartsGenerateContext(
|
||||||
@@ -65,12 +98,10 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
val state: GenerationState,
|
val state: GenerationState,
|
||||||
val descriptor: ClassDescriptor,
|
val descriptor: ClassDescriptor,
|
||||||
val classOrObject: JetClassOrObject,
|
val classOrObject: JetClassOrObject,
|
||||||
val androidClassType: AndroidClassType) {
|
val androidClassType: AndroidClassType)
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun applyProperty(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? {
|
override fun applyProperty(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? {
|
||||||
val resultingDescriptor = resolvedCall.getResultingDescriptor()
|
val resultingDescriptor = resolvedCall.resultingDescriptor
|
||||||
return if (resultingDescriptor is PropertyDescriptor) {
|
return if (resultingDescriptor is PropertyDescriptor) {
|
||||||
return generateSyntheticPropertyCall(receiver, resolvedCall, c, resultingDescriptor)
|
return generateSyntheticPropertyCall(receiver, resolvedCall, c, resultingDescriptor)
|
||||||
}
|
}
|
||||||
@@ -78,7 +109,7 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): Boolean {
|
override fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): Boolean {
|
||||||
val resultingDescriptor = resolvedCall.getResultingDescriptor()
|
val resultingDescriptor = resolvedCall.resultingDescriptor
|
||||||
return if (resultingDescriptor is FunctionDescriptor) {
|
return if (resultingDescriptor is FunctionDescriptor) {
|
||||||
return generateSyntheticFunctionCall(receiver, resolvedCall, c, resultingDescriptor)
|
return generateSyntheticFunctionCall(receiver, resolvedCall, c, resultingDescriptor)
|
||||||
}
|
}
|
||||||
@@ -92,15 +123,15 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
descriptor: FunctionDescriptor
|
descriptor: FunctionDescriptor
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (descriptor.getAndroidPackage() == null) return false
|
if (descriptor.getAndroidPackage() == null) return false
|
||||||
if (descriptor.getName().asString() != AndroidConst.CLEAR_FUNCTION_NAME) return false
|
if (descriptor.name.asString() != AndroidConst.CLEAR_FUNCTION_NAME) return false
|
||||||
|
|
||||||
val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return false
|
val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return false
|
||||||
if (!isCacheSupported(declarationDescriptor)) return true
|
if (!isCacheSupported(declarationDescriptor)) return true
|
||||||
|
|
||||||
val androidClassType = getClassType(declarationDescriptor)
|
val androidClassType = AndroidClassType.getClassType(declarationDescriptor)
|
||||||
if (androidClassType == AndroidClassType.UNKNOWN) return false
|
if (androidClassType == AndroidClassType.UNKNOWN) return false
|
||||||
|
|
||||||
val bytecodeClassName = DescriptorUtils.getFqName(declarationDescriptor).asString().replace('.', '/')
|
val bytecodeClassName = c.typeMapper.mapType(declarationDescriptor).internalName
|
||||||
|
|
||||||
receiver.put(c.typeMapper.mapType(declarationDescriptor), c.v)
|
receiver.put(c.typeMapper.mapType(declarationDescriptor), c.v)
|
||||||
c.v.invokevirtual(bytecodeClassName, CLEAR_CACHE_METHOD_NAME, "()V", false)
|
c.v.invokevirtual(bytecodeClassName, CLEAR_CACHE_METHOD_NAME, "()V", false)
|
||||||
@@ -116,8 +147,7 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
): StackValue? {
|
): StackValue? {
|
||||||
val androidPackage = descriptor.getAndroidPackage() ?: return null
|
val androidPackage = descriptor.getAndroidPackage() ?: return null
|
||||||
val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return null
|
val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return null
|
||||||
val androidClassType = getClassType(declarationDescriptor)
|
val androidClassType = AndroidClassType.getClassType(declarationDescriptor)
|
||||||
|
|
||||||
|
|
||||||
return SyntheticProperty(receiver, c.typeMapper, descriptor, declarationDescriptor, androidClassType, androidPackage)
|
return SyntheticProperty(receiver, c.typeMapper, descriptor, declarationDescriptor, androidClassType, androidPackage)
|
||||||
}
|
}
|
||||||
@@ -129,10 +159,10 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
val declarationDescriptor: ClassifierDescriptor,
|
val declarationDescriptor: ClassifierDescriptor,
|
||||||
val androidClassType: AndroidClassType,
|
val androidClassType: AndroidClassType,
|
||||||
val androidPackage: String
|
val androidPackage: String
|
||||||
) : StackValue(typeMapper.mapType(descriptor.getReturnType()!!)) {
|
) : StackValue(typeMapper.mapType(descriptor.returnType!!)) {
|
||||||
|
|
||||||
override fun putSelector(type: Type, v: InstructionAdapter) {
|
override fun putSelector(type: Type, v: InstructionAdapter) {
|
||||||
val returnTypeString = typeMapper.mapType(descriptor.getType().lowerIfFlexible()).getClassName()
|
val returnTypeString = typeMapper.mapType(descriptor.type.lowerIfFlexible()).className
|
||||||
if (AndroidConst.FRAGMENT_FQNAME == returnTypeString || AndroidConst.SUPPORT_FRAGMENT_FQNAME == returnTypeString) {
|
if (AndroidConst.FRAGMENT_FQNAME == returnTypeString || AndroidConst.SUPPORT_FRAGMENT_FQNAME == returnTypeString) {
|
||||||
return putSelectorForFragment(v)
|
return putSelectorForFragment(v)
|
||||||
}
|
}
|
||||||
@@ -140,8 +170,8 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
if (androidClassType.supportsCache && isCacheSupported(declarationDescriptor)) {
|
if (androidClassType.supportsCache && isCacheSupported(declarationDescriptor)) {
|
||||||
val declarationDescriptorType = typeMapper.mapType(declarationDescriptor)
|
val declarationDescriptorType = typeMapper.mapType(declarationDescriptor)
|
||||||
receiver.put(declarationDescriptorType, v)
|
receiver.put(declarationDescriptorType, v)
|
||||||
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I")
|
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.name.asString(), "I")
|
||||||
v.invokevirtual(declarationDescriptorType.getInternalName(), CACHED_FIND_VIEW_BY_ID_METHOD_NAME, "(I)Landroid/view/View;", false)
|
v.invokevirtual(declarationDescriptorType.internalName, CACHED_FIND_VIEW_BY_ID_METHOD_NAME, "(I)Landroid/view/View;", false)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
when (androidClassType) {
|
when (androidClassType) {
|
||||||
@@ -189,7 +219,7 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getResourceId(v: InstructionAdapter) {
|
fun getResourceId(v: InstructionAdapter) {
|
||||||
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I")
|
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.name.asString(), "I")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,37 +228,7 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun ResolvedCall<*>.getReceiverDeclarationDescriptor(): ClassifierDescriptor? {
|
private fun ResolvedCall<*>.getReceiverDeclarationDescriptor(): ClassifierDescriptor? {
|
||||||
return getExtensionReceiver().getType().getConstructor().getDeclarationDescriptor()
|
return extensionReceiver.type.constructor.declarationDescriptor
|
||||||
}
|
|
||||||
|
|
||||||
private fun getClassType(descriptor: ClassifierDescriptor): AndroidClassType {
|
|
||||||
fun getClassTypeInternal(name: String): AndroidClassType? = when (name) {
|
|
||||||
AndroidConst.ACTIVITY_FQNAME -> AndroidClassType.ACTIVITY
|
|
||||||
AndroidConst.FRAGMENT_FQNAME -> AndroidClassType.FRAGMENT
|
|
||||||
AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY
|
|
||||||
AndroidConst.SUPPORT_FRAGMENT_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT
|
|
||||||
AndroidConst.VIEW_FQNAME -> AndroidClassType.VIEW
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor is LazyJavaClassDescriptor) {
|
|
||||||
val androidClassType = getClassTypeInternal(descriptor.fqName.asString())
|
|
||||||
if (androidClassType != null) return androidClassType
|
|
||||||
}
|
|
||||||
else if (descriptor is LazyClassDescriptor) { // For tests (FakeActivity)
|
|
||||||
val androidClassType = getClassTypeInternal(DescriptorUtils.getFqName(descriptor).toString())
|
|
||||||
if (androidClassType != null) return androidClassType
|
|
||||||
}
|
|
||||||
|
|
||||||
for (supertype in descriptor.getTypeConstructor().getSupertypes()) {
|
|
||||||
val declarationDescriptor = supertype.getConstructor().getDeclarationDescriptor()
|
|
||||||
if (declarationDescriptor != null) {
|
|
||||||
val androidClassType = getClassType(declarationDescriptor)
|
|
||||||
if (androidClassType != AndroidClassType.UNKNOWN) return androidClassType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return AndroidClassType.UNKNOWN
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun generateClassSyntheticParts(
|
override fun generateClassSyntheticParts(
|
||||||
@@ -237,27 +237,58 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
classOrObject: JetClassOrObject,
|
classOrObject: JetClassOrObject,
|
||||||
descriptor: ClassDescriptor
|
descriptor: ClassDescriptor
|
||||||
) {
|
) {
|
||||||
if (descriptor.getKind() != ClassKind.CLASS || descriptor.isInner() || DescriptorUtils.isLocal(descriptor)) return
|
if (descriptor.kind != ClassKind.CLASS || descriptor.isInner || DescriptorUtils.isLocal(descriptor)) return
|
||||||
|
|
||||||
// Do not generate anything if class is not supported
|
// Do not generate anything if class is not supported
|
||||||
val androidClassType = getClassType(descriptor)
|
val androidClassType = AndroidClassType.getClassType(descriptor)
|
||||||
if (androidClassType == AndroidClassType.UNKNOWN) return
|
if (androidClassType == AndroidClassType.UNKNOWN) return
|
||||||
|
|
||||||
val context = SyntheticPartsGenerateContext(classBuilder, state, descriptor, classOrObject, androidClassType)
|
val context = SyntheticPartsGenerateContext(classBuilder, state, descriptor, classOrObject, androidClassType)
|
||||||
context.generateCachedFindViewByIdFunction()
|
context.generateCachedFindViewByIdFunction()
|
||||||
context.generateClearCacheFunction()
|
context.generateClearCacheFunction()
|
||||||
|
|
||||||
|
if (androidClassType.fragment) {
|
||||||
|
val classMembers = descriptor.unsubstitutedMemberScope.getAllDescriptors()
|
||||||
|
val onDestroy = classMembers.firstOrNull { it is FunctionDescriptor && it.isOnDestroyFunction() }
|
||||||
|
if (onDestroy == null) {
|
||||||
|
context.generateOnDestroyFunctionForFragment()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
classBuilder.newField(JvmDeclarationOrigin.NO_ORIGIN, ACC_PRIVATE, PROPERTY_NAME, "Ljava/util/HashMap;", null, null)
|
classBuilder.newField(JvmDeclarationOrigin.NO_ORIGIN, ACC_PRIVATE, PROPERTY_NAME, "Ljava/util/HashMap;", null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SyntheticPartsGenerateContext.generateClearCacheFunction() {
|
private fun FunctionDescriptor.isOnDestroyFunction(): Boolean {
|
||||||
val methodVisitor = classBuilder.newMethod(
|
return kind == CallableMemberDescriptor.Kind.DECLARATION
|
||||||
JvmDeclarationOrigin.NO_ORIGIN, ACC_PUBLIC, CLEAR_CACHE_METHOD_NAME, "()V", null, null)
|
&& name.asString() == ON_DESTROY_METHOD_NAME
|
||||||
|
&& (visibility == Visibilities.INHERITED || visibility == Visibilities.PUBLIC)
|
||||||
|
&& (valueParameters.isEmpty())
|
||||||
|
&& (typeParameters.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
// This generates a simple onDestroy(): Unit = super.onDestroy() function.
|
||||||
|
// CLEAR_CACHE_METHOD_NAME() method call will be inserted in ClassBuilder interceptor.
|
||||||
|
private fun SyntheticPartsGenerateContext.generateOnDestroyFunctionForFragment() {
|
||||||
|
val methodVisitor = classBuilder.newMethod(JvmDeclarationOrigin.NO_ORIGIN, ACC_PUBLIC, ON_DESTROY_METHOD_NAME, "()V", null, null)
|
||||||
methodVisitor.visitCode()
|
methodVisitor.visitCode()
|
||||||
val iv = InstructionAdapter(methodVisitor)
|
val iv = InstructionAdapter(methodVisitor)
|
||||||
|
|
||||||
val classType = state.typeMapper.mapClass(descriptor)
|
val classType = state.typeMapper.mapClass(descriptor)
|
||||||
val className = classType.getInternalName()
|
|
||||||
|
iv.load(0, classType)
|
||||||
|
iv.invokespecial(state.typeMapper.mapClass(descriptor.getSuperClassOrAny()).internalName, ON_DESTROY_METHOD_NAME, "()V", false)
|
||||||
|
iv.areturn(Type.VOID_TYPE)
|
||||||
|
|
||||||
|
FunctionCodegen.endVisit(methodVisitor, ON_DESTROY_METHOD_NAME, classOrObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SyntheticPartsGenerateContext.generateClearCacheFunction() {
|
||||||
|
val methodVisitor = classBuilder.newMethod(JvmDeclarationOrigin.NO_ORIGIN, ACC_PUBLIC, CLEAR_CACHE_METHOD_NAME, "()V", null, null)
|
||||||
|
methodVisitor.visitCode()
|
||||||
|
val iv = InstructionAdapter(methodVisitor)
|
||||||
|
|
||||||
|
val classType = state.typeMapper.mapClass(descriptor)
|
||||||
|
val className = classType.internalName
|
||||||
|
|
||||||
fun loadCache() {
|
fun loadCache() {
|
||||||
iv.load(0, classType)
|
iv.load(0, classType)
|
||||||
@@ -278,7 +309,7 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
|
|
||||||
private fun SyntheticPartsGenerateContext.generateCachedFindViewByIdFunction() {
|
private fun SyntheticPartsGenerateContext.generateCachedFindViewByIdFunction() {
|
||||||
val classType = state.typeMapper.mapClass(descriptor)
|
val classType = state.typeMapper.mapClass(descriptor)
|
||||||
val className = classType.getInternalName()
|
val className = classType.internalName
|
||||||
|
|
||||||
val viewType = Type.getObjectType("android/view/View")
|
val viewType = Type.getObjectType("android/view/View")
|
||||||
|
|
||||||
|
|||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.android.synthetic.codegen
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.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.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
|
import org.jetbrains.kotlin.psi.JetClass
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||||
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
|
|
||||||
|
public class AndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptorExtension {
|
||||||
|
|
||||||
|
override fun interceptClassBuilderFactory(
|
||||||
|
interceptedFactory: ClassBuilderFactory,
|
||||||
|
bindingContext: BindingContext,
|
||||||
|
diagnostics: DiagnosticSink
|
||||||
|
): ClassBuilderFactory {
|
||||||
|
return AndroidOnDestroyClassBuilderFactory(interceptedFactory, bindingContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class AndroidOnDestroyClassBuilderFactory(private val delegateFactory: ClassBuilderFactory, val bindingContext: BindingContext) : ClassBuilderFactory {
|
||||||
|
|
||||||
|
override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder {
|
||||||
|
return AndroidOnDestroyCollectorClassBuilder(delegateFactory.newClassBuilder(origin), bindingContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getClassBuilderMode() = delegateFactory.getClassBuilderMode()
|
||||||
|
|
||||||
|
override fun asText(builder: ClassBuilder?): String? {
|
||||||
|
return delegateFactory.asText((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun asBytes(builder: ClassBuilder?): ByteArray? {
|
||||||
|
return delegateFactory.asBytes((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
delegateFactory.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class AndroidOnDestroyCollectorClassBuilder(
|
||||||
|
internal val delegateClassBuilder: ClassBuilder,
|
||||||
|
val bindingContext: BindingContext
|
||||||
|
) : DelegatingClassBuilder() {
|
||||||
|
private var currentClass: JetClass? = null
|
||||||
|
private var currentClassName: String? = null
|
||||||
|
|
||||||
|
override fun getDelegate() = delegateClassBuilder
|
||||||
|
|
||||||
|
override fun defineClass(
|
||||||
|
origin: PsiElement?,
|
||||||
|
version: Int,
|
||||||
|
access: Int,
|
||||||
|
name: String,
|
||||||
|
signature: String?,
|
||||||
|
superName: String,
|
||||||
|
interfaces: Array<out String>
|
||||||
|
) {
|
||||||
|
if (origin is JetClass) {
|
||||||
|
currentClass = origin
|
||||||
|
currentClassName = name
|
||||||
|
}
|
||||||
|
super.defineClass(origin, version, access, name, signature, superName, interfaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newMethod(
|
||||||
|
origin: JvmDeclarationOrigin,
|
||||||
|
access: Int,
|
||||||
|
name: String,
|
||||||
|
desc: String,
|
||||||
|
signature: String?,
|
||||||
|
exceptions: Array<out String>?
|
||||||
|
): MethodVisitor {
|
||||||
|
return object : MethodVisitor(Opcodes.ASM5, super.newMethod(origin, access, name, desc, signature, exceptions)) {
|
||||||
|
override fun visitCode() {
|
||||||
|
super.visitCode()
|
||||||
|
|
||||||
|
if (name != AndroidExpressionCodegenExtension.ON_DESTROY_METHOD_NAME || currentClass == null) return
|
||||||
|
if (Type.getArgumentTypes(desc).size() != 0) return
|
||||||
|
if (Type.getReturnType(desc) != Type.VOID_TYPE) return
|
||||||
|
|
||||||
|
val classType = currentClassName?.let { Type.getObjectType(it) } ?: return
|
||||||
|
|
||||||
|
val descriptor = bindingContext.get(BindingContext.CLASS, currentClass) as? ClassDescriptor ?: return
|
||||||
|
val androidClassType = AndroidClassType.getClassType(descriptor)
|
||||||
|
if (!androidClassType.fragment) return
|
||||||
|
|
||||||
|
val iv = InstructionAdapter(this)
|
||||||
|
iv.load(0, classType)
|
||||||
|
iv.invokevirtual(currentClassName, AndroidExpressionCodegenExtension.CLEAR_CACHE_METHOD_NAME, "()V", false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package com.myapp
|
||||||
|
|
||||||
|
import android.app.Fragment
|
||||||
|
import java.io.File
|
||||||
|
import kotlinx.android.synthetic.layout.*
|
||||||
|
|
||||||
|
public class MyFragment : Fragment() {
|
||||||
|
init {login}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MyFragment2 : Fragment() {
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
public open fun onDestroy(n: Int) {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 public onDestroy\(\)V
|
||||||
|
// 1 INVOKEVIRTUAL com/myapp/MyFragment\._\$_clearFindViewByIdCache
|
||||||
|
// 1 INVOKEVIRTUAL com/myapp/MyFragment2\._\$_clearFindViewByIdCache
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/item_detail_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ItemDetailActivity"
|
||||||
|
tools:ignore="MergeRootFrame" >
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/login"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Sign in" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
+6
@@ -113,6 +113,12 @@ public class AndroidBytecodeShapeTestGenerated extends AbstractAndroidBytecodeSh
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("onDestroyFragment")
|
||||||
|
public void testOnDestroyFragment() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("plugins/android-compiler-plugin/testData/codegen/bytecodeShape/onDestroyFragment/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("simple")
|
@TestMetadata("simple")
|
||||||
public void testSimple() throws Exception {
|
public void testSimple() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("plugins/android-compiler-plugin/testData/codegen/bytecodeShape/simple/");
|
String fileName = JetTestUtils.navigationMetadata("plugins/android-compiler-plugin/testData/codegen/bytecodeShape/simple/");
|
||||||
|
|||||||
+6
@@ -20,14 +20,18 @@ import com.intellij.openapi.project.Project
|
|||||||
import com.intellij.testFramework.UsefulTestCase
|
import com.intellij.testFramework.UsefulTestCase
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import org.jetbrains.kotlin.android.synthetic.AndroidConfigurationKeys
|
import org.jetbrains.kotlin.android.synthetic.AndroidConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.AndroidExtensionPropertiesComponentContainerContributor
|
||||||
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension
|
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension
|
||||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticFile
|
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticFile
|
||||||
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
|
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
|
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider
|
import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider
|
||||||
|
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
|
||||||
import org.jetbrains.kotlin.psi.JetFile
|
import org.jetbrains.kotlin.psi.JetFile
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -72,6 +76,8 @@ fun UsefulTestCase.createAndroidTestEnvironment(
|
|||||||
val declarationsProvider = AndroidTestExternalDeclarationsProvider(project, resPaths, manifestPath, supportV4)
|
val declarationsProvider = AndroidTestExternalDeclarationsProvider(project, resPaths, manifestPath, supportV4)
|
||||||
ExternalDeclarationsProvider.registerExtension(project, declarationsProvider)
|
ExternalDeclarationsProvider.registerExtension(project, declarationsProvider)
|
||||||
ExpressionCodegenExtension.registerExtension(project, AndroidExpressionCodegenExtension())
|
ExpressionCodegenExtension.registerExtension(project, AndroidExpressionCodegenExtension())
|
||||||
|
StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor())
|
||||||
|
ClassBuilderInterceptorExtension.registerExtension(project, AndroidOnDestroyClassBuilderInterceptorExtension())
|
||||||
|
|
||||||
return myEnvironment
|
return myEnvironment
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,5 +33,6 @@
|
|||||||
<simpleNameReferenceExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidSimpleNameReferenceExtension"/>
|
<simpleNameReferenceExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidSimpleNameReferenceExtension"/>
|
||||||
<defaultErrorMessages implementation="org.jetbrains.kotlin.android.synthetic.diagnostic.DefaultErrorMessagesAndroid"/>
|
<defaultErrorMessages implementation="org.jetbrains.kotlin.android.synthetic.diagnostic.DefaultErrorMessagesAndroid"/>
|
||||||
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.android.synthetic.AndroidExtensionPropertiesComponentContainerContributor"/>
|
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.android.synthetic.AndroidExtensionPropertiesComponentContainerContributor"/>
|
||||||
|
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
|||||||
Reference in New Issue
Block a user