Fix Android byte code generation inside complex expressions

This commit is contained in:
Yan Zhulanow
2015-03-24 16:43:16 +03:00
parent de06a0cbc2
commit 96074bc948
3 changed files with 68 additions and 42 deletions
@@ -1914,8 +1914,9 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v); ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v);
JetType returnType = propertyDescriptor.getReturnType(); JetType returnType = propertyDescriptor.getReturnType();
for (ExpressionCodegenExtension extension : codegenExtensions) { for (ExpressionCodegenExtension extension : codegenExtensions) {
if (returnType != null && extension.apply(receiver, resolvedCall, context)) { if (returnType != null) {
return StackValue.onStack(typeMapper.mapType(returnType)); StackValue value = extension.applyProperty(receiver, resolvedCall, context);
if (value != null) return value;
} }
} }
} }
@@ -2352,7 +2353,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
if (!codegenExtensions.isEmpty()) { if (!codegenExtensions.isEmpty()) {
ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v); ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v);
for (ExpressionCodegenExtension extension : codegenExtensions) { for (ExpressionCodegenExtension extension : codegenExtensions) {
if (extension.apply(receiver, resolvedCall, context)) return; if (extension.applyFunction(receiver, resolvedCall, context)) return;
} }
} }
@@ -35,9 +35,17 @@ public trait ExpressionCodegenExtension {
public val v: InstructionAdapter 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. * Used for generating custom byte code for the property value obtain. This function has lazy semantics.
public fun apply(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): Boolean * 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( public fun generateClassSyntheticParts(
classBuilder: ClassBuilder, classBuilder: ClassBuilder,
@@ -55,6 +55,8 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
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" private val CLEAR_CACHE_METHOD_NAME = "_\$_clearFindViewByIdCache"
fun isCacheSupported(descriptor: ClassifierDescriptor) = descriptor.getSource() is KotlinSourceElement
} }
private class SyntheticPartsGenerateContext( 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() val resultingDescriptor = resolvedCall.getResultingDescriptor()
return when (resultingDescriptor) { return if (resultingDescriptor is PropertyDescriptor) {
is PropertyDescriptor -> generateSyntheticPropertyCall(receiver, resolvedCall, c, resultingDescriptor) return generateSyntheticPropertyCall(receiver, resolvedCall, c, resultingDescriptor)
is FunctionDescriptor -> generateSyntheticFunctionCall(receiver, resolvedCall, c, resultingDescriptor)
else -> false
} }
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( private fun generateSyntheticFunctionCall(
@@ -103,41 +112,51 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
c: ExpressionCodegenExtension.Context, c: ExpressionCodegenExtension.Context,
descriptor: PropertyDescriptor descriptor: PropertyDescriptor
): Boolean { ): StackValue? {
val androidPackage = descriptor.getAndroidPackage() ?: return false val androidPackage = descriptor.getAndroidPackage() ?: return null
val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return false val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return null
val androidClassType = getClassType(declarationDescriptor) 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") return SyntheticProperty(receiver, c.typeMapper, descriptor, declarationDescriptor, androidClassType, androidPackage)
c.v.invokevirtual(bytecodeClassName, CACHED_FIND_VIEW_BY_ID_METHOD_NAME, "(I)Landroid/view/View;", false) }
}
else { private class SyntheticProperty(
when (androidClassType) { val receiver: StackValue,
AndroidClassType.ACTIVITY, AndroidClassType.VIEW -> { val typeMapper: JetTypeMapper,
receiver.put(Type.getType("L${androidClassType.internalClassName};"), c.v) val descriptor: PropertyDescriptor,
c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I") val declarationDescriptor: ClassifierDescriptor,
c.v.invokevirtual(androidClassType.internalClassName, "findViewById", "(I)Landroid/view/View;", false) 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? { private fun CallableDescriptor.getAndroidPackage(): String? {
@@ -148,8 +167,6 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
return getExtensionReceiver().getType().getConstructor().getDeclarationDescriptor() return getExtensionReceiver().getType().getConstructor().getDeclarationDescriptor()
} }
private fun isCacheSupported(descriptor: ClassifierDescriptor) = descriptor.getSource() is KotlinSourceElement
private fun getClassType(descriptor: ClassifierDescriptor): AndroidClassType { private fun getClassType(descriptor: ClassifierDescriptor): AndroidClassType {
fun getClassTypeInternal(name: String): AndroidClassType? = when (name) { fun getClassTypeInternal(name: String): AndroidClassType? = when (name) {
"android.app.Activity" -> AndroidClassType.ACTIVITY "android.app.Activity" -> AndroidClassType.ACTIVITY