Fix Android byte code generation inside complex expressions
This commit is contained in:
@@ -1914,8 +1914,9 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> 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<StackValue, StackValue> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-3
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user