diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AbstractAndroidExtensionsExpressionCodegenExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AbstractAndroidExtensionsExpressionCodegenExtension.kt index 361c69d236d..0c0354e7374 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AbstractAndroidExtensionsExpressionCodegenExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AbstractAndroidExtensionsExpressionCodegenExtension.kt @@ -21,7 +21,6 @@ import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtElement 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.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf @@ -153,38 +152,6 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC context.generateCachedFindViewByIdFunction() context.generateClearCacheFunction() context.generateCacheField() - - if (containerOptions.containerType.isFragment) { - val classMembers = container.unsubstitutedMemberScope.getContributedDescriptors() - val onDestroy = classMembers.firstOrNull { it is FunctionDescriptor && it.isOnDestroyFunction() } - if (onDestroy == null) { - context.generateOnDestroyFunctionForFragment() - } - } - } - - private fun FunctionDescriptor.isOnDestroyFunction(): Boolean { - return kind == CallableMemberDescriptor.Kind.DECLARATION - && name.asString() == ON_DESTROY_METHOD_NAME - && (visibility == DescriptorVisibilities.INHERITED || visibility == DescriptorVisibilities.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 or ACC_SYNTHETIC, ON_DESTROY_METHOD_NAME, "()V", null, null) - methodVisitor.visitCode() - val iv = InstructionAdapter(methodVisitor) - - val containerType = state.typeMapper.mapClass(container) - - iv.load(0, containerType) - iv.invokespecial(state.typeMapper.mapClass(container.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() { diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidIrExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidIrExtension.kt index 42667cb55a5..19363574882 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidIrExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidIrExtension.kt @@ -16,7 +16,6 @@ import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension 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.common.lower.irBlock import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI @@ -28,6 +27,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl 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.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.IrTypeOperator import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol @@ -73,8 +73,15 @@ private class AndroidIrTransformer(val extension: AndroidIrExtension, val plugin private val cachedMethods = mutableMapOf() private val cachedFields = mutableMapOf() + private val cachedCacheFields = mutableMapOf() + private val cachedCacheClearFuns = mutableMapOf() + private val cachedCacheLookupFuns = mutableMapOf() + private val irFactory: IrFactory = IrFactoryImpl + private fun irBuilder(scope: IrSymbol, replacing: IrStatement): IrBuilderWithScope = + DeclarationIrBuilder(IrGeneratorContextBase(pluginContext.irBuiltIns), scope, replacing.startOffset, replacing.endOffset) + private fun createPackage(fqName: FqName) = cachedPackages.getOrPut(fqName) { IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment(pluginContext.moduleDescriptor, fqName) @@ -111,6 +118,79 @@ private class AndroidIrTransformer(val extension: AndroidIrExtension, val plugin createClass(fqName.parent()).addField(fqName.shortName(), type, DescriptorVisibilities.PUBLIC) } + // NOTE: sparse array version intentionally not implemented; this plugin is deprecated + private val mapFactory = pluginContext.irBuiltIns.findFunctions(Name.identifier("mutableMapOf"), "kotlin", "collections") + .single { it.descriptor.valueParameters.isEmpty() } // unbound - can't use `owner` + private val mapGet = pluginContext.irBuiltIns.mapClass.owner.functions + .single { it.name.asString() == "get" && it.valueParameters.size == 1 } + private val mapSet = pluginContext.irBuiltIns.mutableMapClass.owner.functions + .single { it.name.asString() == "put" && it.valueParameters.size == 2 } + private val mapClear = pluginContext.irBuiltIns.mutableMapClass.owner.functions + .single { it.name.asString() == "clear" && it.valueParameters.isEmpty() } + + private fun IrClass.getCacheField(): IrField = + cachedCacheFields.getOrPut(this) { + val viewType = createClass(FqName(AndroidConst.VIEW_FQNAME)).defaultType.makeNullable() + irFactory.buildField { + name = Name.identifier(AbstractAndroidExtensionsExpressionCodegenExtension.PROPERTY_NAME) + type = pluginContext.irBuiltIns.mutableMapClass.typeWith(pluginContext.irBuiltIns.intType, viewType) + }.apply { + parent = this@getCacheField + initializer = irBuilder(symbol, this).run { + irExprBody(irCall(mapFactory, type, valueArgumentsCount = 0, typeArgumentsCount = 2).apply { + putTypeArgument(0, context.irBuiltIns.intType) + putTypeArgument(1, viewType) + }) + } + } + } + + private fun IrClass.getClearCacheFun(): IrSimpleFunction = + cachedCacheClearFuns.getOrPut(this) { + irFactory.buildFun { + name = Name.identifier(AbstractAndroidExtensionsExpressionCodegenExtension.CLEAR_CACHE_METHOD_NAME) + modality = Modality.OPEN + returnType = pluginContext.irBuiltIns.unitType + }.apply { + val self = addDispatchReceiver { type = defaultType } + parent = this@getClearCacheFun + body = irBuilder(symbol, this).irBlockBody { + +irCall(mapClear).apply { dispatchReceiver = irGetField(irGet(self), getCacheField()) } + } + } + } + + private fun IrClass.getCachedFindViewByIdFun(): IrSimpleFunction = + cachedCacheLookupFuns.getOrPut(this) { + val containerType = ContainerOptionsProxy.create(descriptor).containerType + val viewType = createClass(FqName(AndroidConst.VIEW_FQNAME)).defaultType.makeNullable() + irFactory.buildFun { + name = Name.identifier(AbstractAndroidExtensionsExpressionCodegenExtension.CACHED_FIND_VIEW_BY_ID_METHOD_NAME) + modality = Modality.OPEN + returnType = viewType + }.apply { + val self = addDispatchReceiver { type = defaultType } + val resourceId = addValueParameter("id", pluginContext.irBuiltIns.intType) + parent = this@getCachedFindViewByIdFun + body = irBuilder(symbol, this).irBlockBody { + val cache = irTemporary(irGetField(irGet(self), getCacheField())) + // cache[resourceId] ?: findViewById(resourceId)?.also { cache[resourceId] = it } + +irReturn(irElvis(viewType, irCallOp(mapGet.symbol, viewType, irGet(cache), irGet(resourceId))) { + irSafeLet(viewType, irFindViewById(irGet(self), irGet(resourceId), containerType)) { foundView -> + irBlock { + +irCall(mapSet.symbol).apply { + dispatchReceiver = irGet(cache) + putValueArgument(0, irGet(resourceId)) + putValueArgument(1, irGet(foundView)) + } + +irGet(foundView) + } + } + }) + } + } + } + override fun visitClassNew(declaration: IrClass): IrStatement { if (!declaration.isClass && !declaration.isObject) return super.visitClassNew(declaration) @@ -120,39 +200,42 @@ private class AndroidIrTransformer(val extension: AndroidIrExtension, val plugin if (containerOptions.containerType == AndroidContainerType.LAYOUT_CONTAINER && !extension.isExperimental(declaration)) return super.visitClassNew(declaration) - // TODO 1. actually generate the cache; - // 2. clear it in the function added below; - // 3. if containerOptions.containerType.isFragment and there's no onDestroy, add one that clears the cache - declaration.addFunction { - name = Name.identifier(AbstractAndroidExtensionsExpressionCodegenExtension.CLEAR_CACHE_METHOD_NAME) - modality = Modality.OPEN - returnType = pluginContext.irBuiltIns.unitType - }.apply { - addDispatchReceiver { type = declaration.defaultType } - body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) + if ((containerOptions.cache ?: extension.getGlobalCacheImpl(declaration)).hasCache) { + declaration.declarations += declaration.getCacheField() + declaration.declarations += declaration.getClearCacheFun() + declaration.declarations += declaration.getCachedFindViewByIdFun() } return super.visitClassNew(declaration) } override fun visitCall(expression: IrCall): IrExpression { + val receiverClass = expression.extensionReceiver?.type?.classOrNull + ?: return super.visitFunctionAccess(expression) + val receiver = expression.extensionReceiver!!.transform(this, null) + + val containerOptions = ContainerOptionsProxy.create(receiverClass.descriptor) + val containerHasCache = (containerOptions.cache ?: extension.getGlobalCacheImpl(receiverClass.owner)).hasCache + if (expression.symbol.descriptor is AndroidSyntheticFunction) { - // TODO actually call the appropriate CLEAR_CACHE_METHOD_NAME-named function - return IrBlockImpl(expression.startOffset, expression.endOffset, pluginContext.irBuiltIns.unitType) + if (expression.symbol.owner.name.asString() != AndroidConst.CLEAR_FUNCTION_NAME) + return super.visitFunctionAccess(expression) + if (!containerHasCache) + return IrBlockImpl(expression.startOffset, expression.endOffset, expression.type) + return receiverClass.owner.getClearCacheFun().callWithRanges(expression).apply { + dispatchReceiver = receiver + } } val resource = (expression.symbol.descriptor as? PropertyGetterDescriptor)?.correspondingProperty as? AndroidSyntheticProperty ?: return super.visitFunctionAccess(expression) val packageFragment = (resource as PropertyDescriptor).containingDeclaration as? AndroidSyntheticPackageFragmentDescriptor ?: return super.visitFunctionAccess(expression) - val receiverClass = expression.extensionReceiver?.type?.classOrNull - ?: return super.visitFunctionAccess(expression) - val receiver = expression.extensionReceiver!!.transform(this, null) val packageFqName = FqName(packageFragment.packageData.moduleData.module.applicationPackage) val field = createField(packageFqName.child("R\$id").child(resource.name), pluginContext.irBuiltIns.intType) val resourceId = IrGetFieldImpl(expression.startOffset, expression.endOffset, field.symbol, field.type) - val containerType = ContainerOptionsProxy.create(receiverClass.descriptor).containerType + val containerType = containerOptions.containerType val result = if (expression.type.classifierOrNull?.isFragment == true) { // this.get[Support]FragmentManager().findFragmentById(R$id.) val appPackageFqName = when (containerType) { @@ -180,47 +263,65 @@ private class AndroidIrTransformer(val extension: AndroidIrExtension, val plugin } putValueArgument(0, resourceId) } - } else { - // this[.getView()?|.getContainerView()?].findViewById(R$id.) - val viewClass = createClass(FqName(AndroidConst.VIEW_FQNAME)) - val getView = when (containerType) { - AndroidContainerType.ACTIVITY, AndroidContainerType.ANDROIDX_SUPPORT_FRAGMENT_ACTIVITY, AndroidContainerType.SUPPORT_FRAGMENT_ACTIVITY, AndroidContainerType.VIEW, AndroidContainerType.DIALOG -> - null - AndroidContainerType.FRAGMENT, AndroidContainerType.ANDROIDX_SUPPORT_FRAGMENT, AndroidContainerType.SUPPORT_FRAGMENT -> - createMethod(containerType.fqName.child("getView"), viewClass.defaultType.makeNullable()) - AndroidContainerType.LAYOUT_CONTAINER -> - createMethod(containerType.fqName.child("getContainerView"), viewClass.defaultType.makeNullable(), true) - else -> throw IllegalStateException("Invalid Android class type: $containerType") // Should never occur - } - val findViewByIdParent = if (getView == null) containerType.fqName else FqName(AndroidConst.VIEW_FQNAME) - val findViewById = createMethod(findViewByIdParent.child("findViewById"), viewClass.defaultType.makeNullable()) { - addValueParameter("id", pluginContext.irBuiltIns.intType) - }.callWithRanges(expression).apply { + } else if (containerHasCache) { + // this._$_findCachedViewById(R$id.) + receiverClass.owner.getCachedFindViewByIdFun().callWithRanges(expression).apply { + dispatchReceiver = receiver putValueArgument(0, resourceId) } - if (getView == null) { - findViewById.apply { dispatchReceiver = receiver } - } else { - val scope = currentScope!!.scope.scopeOwnerSymbol - DeclarationIrBuilder(IrGeneratorContextBase(pluginContext.irBuiltIns), scope).irBlock(expression) { - val variable = irTemporary(getView.callWithRanges(expression).apply { - dispatchReceiver = receiver - }) - +irIfNull(findViewById.type, irGet(variable), irNull(), findViewById.apply { - dispatchReceiver = irGet(variable) - }) - } - } + } else { + irBuilder(currentScope!!.scope.scopeOwnerSymbol, expression).irFindViewById(receiver, resourceId, containerType) } return with(expression) { IrTypeOperatorCallImpl(startOffset, endOffset, type, IrTypeOperator.CAST, type, result) } } + + private fun IrBuilderWithScope.irFindViewById( + receiver: IrExpression, id: IrExpression, container: AndroidContainerType + ): IrExpression { + // this[.getView()?|.getContainerView()?].findViewById(R$id.) + val viewClass = createClass(FqName(AndroidConst.VIEW_FQNAME)) + val getView = when (container) { + AndroidContainerType.FRAGMENT, + AndroidContainerType.ANDROIDX_SUPPORT_FRAGMENT, + AndroidContainerType.SUPPORT_FRAGMENT -> + createMethod(container.fqName.child("getView"), viewClass.defaultType.makeNullable()) + AndroidContainerType.LAYOUT_CONTAINER -> + createMethod(container.fqName.child("getContainerView"), viewClass.defaultType.makeNullable(), true) + else -> null + } + val findViewByIdParent = if (getView == null) container.fqName else FqName(AndroidConst.VIEW_FQNAME) + val findViewById = createMethod(findViewByIdParent.child("findViewById"), viewClass.defaultType.makeNullable()) { + addValueParameter("id", pluginContext.irBuiltIns.intType) + } + val findViewCall = irCall(findViewById).apply { putValueArgument(0, id) } + return if (getView == null) { + findViewCall.apply { dispatchReceiver = receiver } + } else { + irSafeLet(findViewCall.type, irCall(getView).apply { dispatchReceiver = receiver }) { parent -> + findViewCall.apply { dispatchReceiver = irGet(parent) } + } + } + } } private fun FqName.child(name: String) = child(Name.identifier(name)) -@ObsoleteDescriptorBasedAPI private fun IrSimpleFunction.callWithRanges(source: IrExpression) = - IrCallImpl.fromSymbolDescriptor(source.startOffset, source.endOffset, returnType, symbol) + IrCallImpl.fromSymbolOwner(source.startOffset, source.endOffset, returnType, symbol) + +private inline fun IrBuilderWithScope.irSafeCall( + type: IrType, lhs: IrExpression, + ifNull: IrBuilderWithScope.() -> IrExpression, + ifNotNull: IrBuilderWithScope.(IrVariable) -> IrExpression +) = irBlock(origin = IrStatementOrigin.SAFE_CALL) { + +irTemporary(lhs).let { irIfNull(type, irGet(it), ifNull(), ifNotNull(it)) } +} + +private inline fun IrBuilderWithScope.irSafeLet(type: IrType, lhs: IrExpression, rhs: IrBuilderWithScope.(IrVariable) -> IrExpression) = + irSafeCall(type, lhs, { irNull() }, rhs) + +private inline fun IrBuilderWithScope.irElvis(type: IrType, lhs: IrExpression, rhs: IrBuilderWithScope.() -> IrExpression) = + irSafeCall(type, lhs, rhs) { irGet(it) } private val AndroidContainerType.fqName: FqName get() = FqName(internalClassName.replace("/", ".")) diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidOnDestroyClassBuilderInterceptorExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidOnDestroyClassBuilderInterceptorExtension.kt index e25a3d840b5..4a3905b6814 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidOnDestroyClassBuilderInterceptorExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidOnDestroyClassBuilderInterceptorExtension.kt @@ -21,65 +21,48 @@ import kotlinx.android.extensions.CacheImplementation import org.jetbrains.kotlin.android.synthetic.codegen.AbstractAndroidExtensionsExpressionCodegenExtension.Companion.CLEAR_CACHE_METHOD_NAME import org.jetbrains.kotlin.android.synthetic.codegen.AbstractAndroidExtensionsExpressionCodegenExtension.Companion.ON_DESTROY_METHOD_NAME import org.jetbrains.kotlin.android.synthetic.descriptors.ContainerOptionsProxy -import org.jetbrains.kotlin.codegen.AbstractClassBuilder import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.ClassBuilderFactory import org.jetbrains.kotlin.codegen.DelegatingClassBuilder +import org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.diagnostics.DiagnosticSink -import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtElement 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.RecordComponentVisitor -import org.jetbrains.org.objectweb.asm.Type -import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter abstract class AbstractAndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptorExtension { override fun interceptClassBuilderFactory( interceptedFactory: ClassBuilderFactory, bindingContext: BindingContext, diagnostics: DiagnosticSink - ): ClassBuilderFactory { - return AndroidOnDestroyClassBuilderFactory(interceptedFactory, bindingContext) - } + ): ClassBuilderFactory = AndroidOnDestroyClassBuilderFactory(interceptedFactory) abstract fun getGlobalCacheImpl(element: KtElement): CacheImplementation - private inner class AndroidOnDestroyClassBuilderFactory( - private val delegateFactory: ClassBuilderFactory, - val bindingContext: BindingContext - ) : ClassBuilderFactory { + private inner class AndroidOnDestroyClassBuilderFactory(delegate: ClassBuilderFactory) : DelegatingClassBuilderFactory(delegate) { + override fun newClassBuilder(origin: JvmDeclarationOrigin): DelegatingClassBuilder = + AndroidOnDestroyCollectorClassBuilder(delegate.newClassBuilder(origin), origin.hasCache) - override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder { - return AndroidOnDestroyCollectorClassBuilder(delegateFactory.newClassBuilder(origin), bindingContext) - } - - override fun getClassBuilderMode() = delegateFactory.classBuilderMode - - override fun asText(builder: ClassBuilder?): String? { - return delegateFactory.asText((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder) - } - - override fun asBytes(builder: ClassBuilder?): ByteArray? { - return delegateFactory.asBytes((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder) - } - - override fun close() { - delegateFactory.close() - } + private val JvmDeclarationOrigin.hasCache: Boolean + get() = descriptor is ClassDescriptor && element is KtElement && + ContainerOptionsProxy.create(descriptor as ClassDescriptor).let { + it.containerType.isFragment && (it.cache ?: getGlobalCacheImpl(element as KtElement)).hasCache + } } - private inner class AndroidOnDestroyCollectorClassBuilder( - internal val delegateClassBuilder: ClassBuilder, - val bindingContext: BindingContext + private class AndroidOnDestroyCollectorClassBuilder( + private val delegate: ClassBuilder, + private val hasCache: Boolean ) : DelegatingClassBuilder() { - private var currentClass: KtClass? = null - private var currentClassName: String? = null + private lateinit var currentClassName: String + private lateinit var superClassName: String + private var hasOnDestroy = false - override fun getDelegate() = delegateClassBuilder + override fun getDelegate() = delegate override fun defineClass( origin: PsiElement?, @@ -90,13 +73,27 @@ abstract class AbstractAndroidOnDestroyClassBuilderInterceptorExtension : ClassB superName: String, interfaces: Array ) { - if (origin is KtClass) { - currentClass = origin - currentClassName = name - } + currentClassName = name + superClassName = superName super.defineClass(origin, version, access, name, signature, superName, interfaces) } + override fun done() { + if (hasCache && !hasOnDestroy) { + val mv = newMethod( + JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_PUBLIC or Opcodes.ACC_SYNTHETIC, ON_DESTROY_METHOD_NAME, "()V", + null, null + ) + mv.visitCode() + mv.visitVarInsn(Opcodes.ALOAD, 0) + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassName, ON_DESTROY_METHOD_NAME, "()V", false) + mv.visitInsn(Opcodes.RETURN) + mv.visitMaxs(1, 1) + mv.visitEnd() + } + super.done() + } + override fun newMethod( origin: JvmDeclarationOrigin, access: Int, @@ -105,37 +102,18 @@ abstract class AbstractAndroidOnDestroyClassBuilderInterceptorExtension : ClassB signature: String?, exceptions: Array? ): MethodVisitor { - return object : MethodVisitor(Opcodes.API_VERSION, super.newMethod(origin, access, name, desc, signature, exceptions)) { + val mv = super.newMethod(origin, access, name, desc, signature, exceptions) + if (!hasCache || name != ON_DESTROY_METHOD_NAME || desc != "()V") return mv + hasOnDestroy = true + return object : MethodVisitor(Opcodes.API_VERSION, mv) { override fun visitInsn(opcode: Int) { if (opcode == Opcodes.RETURN) { - generateClearCacheMethodCall() + visitVarInsn(Opcodes.ALOAD, 0) + visitMethodInsn(Opcodes.INVOKEVIRTUAL, currentClassName, CLEAR_CACHE_METHOD_NAME, "()V", false) } - super.visitInsn(opcode) } - - private fun generateClearCacheMethodCall() { - val currentClass = currentClass - if (name != ON_DESTROY_METHOD_NAME || currentClass == null) return - if (Type.getArgumentTypes(desc).isNotEmpty()) return - if (Type.getReturnType(desc) != Type.VOID_TYPE) return - - val containerType = currentClassName?.let { Type.getObjectType(it) } ?: return - - val container = bindingContext.get(BindingContext.CLASS, currentClass) ?: return - val entityOptions = ContainerOptionsProxy.create(container) - if (!entityOptions.containerType.isFragment || !(entityOptions.cache ?: getGlobalCacheImpl(currentClass)).hasCache) return - - val iv = InstructionAdapter(this) - iv.load(0, containerType) - iv.invokevirtual(currentClassName, CLEAR_CACHE_METHOD_NAME, "()V", false) - } } } - - override fun newRecordComponent(name: String, desc: String, signature: String?): RecordComponentVisitor { - return AbstractClassBuilder.EMPTY_RECORD_VISITOR - } } - }