From 96074bc9485b2ef1f5b58afa35222c3fe263efa7 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Tue, 24 Mar 2015 16:43:16 +0300 Subject: [PATCH] Fix Android byte code generation inside complex expressions --- .../kotlin/codegen/ExpressionCodegen.java | 7 +- .../extensions/ExpressionCodegenExtension.kt | 14 ++- .../src/AndroidExpressionCodegenExtension.kt | 89 +++++++++++-------- 3 files changed, 68 insertions(+), 42 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 683926b06ab..6107f9cf723 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -1914,8 +1914,9 @@ public class ExpressionCodegen extends JetVisitor implem ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v); JetType returnType = propertyDescriptor.getReturnType(); for (ExpressionCodegenExtension extension : codegenExtensions) { - if (returnType != null && extension.apply(receiver, resolvedCall, context)) { - return StackValue.onStack(typeMapper.mapType(returnType)); + if (returnType != null) { + StackValue value = extension.applyProperty(receiver, resolvedCall, context); + if (value != null) return value; } } } @@ -2352,7 +2353,7 @@ public class ExpressionCodegen extends JetVisitor implem if (!codegenExtensions.isEmpty()) { ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v); for (ExpressionCodegenExtension extension : codegenExtensions) { - if (extension.apply(receiver, resolvedCall, context)) return; + if (extension.applyFunction(receiver, resolvedCall, context)) return; } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/extensions/ExpressionCodegenExtension.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/extensions/ExpressionCodegenExtension.kt index 814324beae8..946640931fa 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/extensions/ExpressionCodegenExtension.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/extensions/ExpressionCodegenExtension.kt @@ -35,9 +35,17 @@ public trait ExpressionCodegenExtension { public val v: InstructionAdapter ) - // Function is responsible to put the value on stack by itself. - // Returns false if not applicable, and if stack was not modified. - public fun apply(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): Boolean + /** + * Used for generating custom byte code for the property value obtain. This function has lazy semantics. + * Returns new stack value. + */ + public fun applyProperty(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): StackValue? = null + + /** + * Used for generating custom byte code for the function call. This function has non-lazy semantics. + * Returns true if the stack was modified. + */ + public fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): Boolean = false public fun generateClassSyntheticParts( classBuilder: ClassBuilder, diff --git a/plugins/android-compiler-plugin/src/AndroidExpressionCodegenExtension.kt b/plugins/android-compiler-plugin/src/AndroidExpressionCodegenExtension.kt index 97cec39e629..4f6bf3d347b 100644 --- a/plugins/android-compiler-plugin/src/AndroidExpressionCodegenExtension.kt +++ b/plugins/android-compiler-plugin/src/AndroidExpressionCodegenExtension.kt @@ -55,6 +55,8 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { private val PROPERTY_NAME = "_\$_findViewCache" private val CACHED_FIND_VIEW_BY_ID_METHOD_NAME = "_\$_findCachedViewById" private val CLEAR_CACHE_METHOD_NAME = "_\$_clearFindViewByIdCache" + + fun isCacheSupported(descriptor: ClassifierDescriptor) = descriptor.getSource() is KotlinSourceElement } private class SyntheticPartsGenerateContext( @@ -66,13 +68,20 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { } - override fun apply(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): Boolean { + override fun applyProperty(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? { val resultingDescriptor = resolvedCall.getResultingDescriptor() - return when (resultingDescriptor) { - is PropertyDescriptor -> generateSyntheticPropertyCall(receiver, resolvedCall, c, resultingDescriptor) - is FunctionDescriptor -> generateSyntheticFunctionCall(receiver, resolvedCall, c, resultingDescriptor) - else -> false + return if (resultingDescriptor is PropertyDescriptor) { + return generateSyntheticPropertyCall(receiver, resolvedCall, c, resultingDescriptor) } + else null + } + + override fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): Boolean { + val resultingDescriptor = resolvedCall.getResultingDescriptor() + return if (resultingDescriptor is FunctionDescriptor) { + return generateSyntheticFunctionCall(receiver, resolvedCall, c, resultingDescriptor) + } + else false } private fun generateSyntheticFunctionCall( @@ -103,41 +112,51 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context, descriptor: PropertyDescriptor - ): Boolean { - val androidPackage = descriptor.getAndroidPackage() ?: return false - val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return false - + ): StackValue? { + val androidPackage = descriptor.getAndroidPackage() ?: return null + val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return null val androidClassType = getClassType(declarationDescriptor) - if (androidClassType.supportsCache && isCacheSupported(declarationDescriptor)) { - val className = DescriptorUtils.getFqName(declarationDescriptor).toString() - val bytecodeClassName = className.replace('.', '/') - receiver.put(c.typeMapper.mapType(declarationDescriptor), c.v) - c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I") - c.v.invokevirtual(bytecodeClassName, CACHED_FIND_VIEW_BY_ID_METHOD_NAME, "(I)Landroid/view/View;", false) - } - else { - when (androidClassType) { - AndroidClassType.ACTIVITY, AndroidClassType.VIEW -> { - receiver.put(Type.getType("L${androidClassType.internalClassName};"), c.v) - c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I") - c.v.invokevirtual(androidClassType.internalClassName, "findViewById", "(I)Landroid/view/View;", false) + + return SyntheticProperty(receiver, c.typeMapper, descriptor, declarationDescriptor, androidClassType, androidPackage) + } + + private class SyntheticProperty( + val receiver: StackValue, + val typeMapper: JetTypeMapper, + val descriptor: PropertyDescriptor, + val declarationDescriptor: ClassifierDescriptor, + val androidClassType: AndroidClassType, + val androidPackage: String + ) : StackValue(typeMapper.mapType(descriptor.getReturnType()!!)) { + + override fun putSelector(type: Type, v: InstructionAdapter) { + if (androidClassType.supportsCache && isCacheSupported(declarationDescriptor)) { + + val declarationDescriptorType = typeMapper.mapType(declarationDescriptor) + receiver.put(declarationDescriptorType, v) + v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I") + v.invokevirtual(declarationDescriptorType.getInternalName(), CACHED_FIND_VIEW_BY_ID_METHOD_NAME, "(I)Landroid/view/View;", false) + } + else { + when (androidClassType) { + AndroidClassType.ACTIVITY, AndroidClassType.VIEW -> { + receiver.put(Type.getType("L${androidClassType.internalClassName};"), v) + v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I") + v.invokevirtual(androidClassType.internalClassName, "findViewById", "(I)Landroid/view/View;", false) + } + AndroidClassType.FRAGMENT -> { + receiver.put(Type.getType("L${androidClassType.internalClassName};"), v) + v.invokevirtual(androidClassType.internalClassName, "getView", "()Landroid/view/View;", false) + v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I") + v.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false) + } + else -> throw IllegalStateException("Invalid Android class type: $androidClassType") // Should never occur } - AndroidClassType.FRAGMENT -> { - receiver.put(Type.getType("L${androidClassType.internalClassName};"), c.v) - c.v.invokevirtual(androidClassType.internalClassName, "getView", "()Landroid/view/View;", false) - c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I") - c.v.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false) - } - else -> return false } - + v.checkcast(this.type) } - - val retType = c.typeMapper.mapType(descriptor.getReturnType()!!) - c.v.checkcast(retType) - return true } private fun CallableDescriptor.getAndroidPackage(): String? { @@ -148,8 +167,6 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { return getExtensionReceiver().getType().getConstructor().getDeclarationDescriptor() } - private fun isCacheSupported(descriptor: ClassifierDescriptor) = descriptor.getSource() is KotlinSourceElement - private fun getClassType(descriptor: ClassifierDescriptor): AndroidClassType { fun getClassTypeInternal(name: String): AndroidClassType? = when (name) { "android.app.Activity" -> AndroidClassType.ACTIVITY