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);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-3
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user