Cache resolved Views in Map

This commit is contained in:
Yan Zhulanow
2014-12-26 15:40:11 +03:00
parent cb0d0543fd
commit 438d2ee5fa
8 changed files with 151 additions and 19 deletions
@@ -36,6 +36,20 @@ import org.jetbrains.jet.codegen.StackValue
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
import org.jetbrains.jet.codegen.ClassBuilder
import org.jetbrains.org.objectweb.asm.Opcodes.*
import org.jetbrains.jet.lang.resolve.java.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.jet.lang.psi.JetClassOrObject
import org.jetbrains.jet.codegen.FunctionCodegen
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.jet.lang.psi.JetClass
import org.jetbrains.jet.lang.psi.JetClassBody
import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyClassDescriptor
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor
import org.jetbrains.jet.lang.resolve.java.lazy.descriptors.LazyJavaClassDescriptor
import org.jetbrains.jet.lang.resolve.DescriptorUtils
public object AndroidConfigurationKeys {
@@ -73,25 +87,129 @@ public class AndroidDeclarationsProvider(private val project: Project) : Externa
}
public class AndroidExpressionCodegen : ExpressionCodegenExtension {
val propertyName = "_\$_findViewByIdCache"
val methodName = "_\$_findCachedViewById"
override fun apply(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? {
if (resolvedCall.getResultingDescriptor() !is PropertyDescriptor) return null
if (resolvedCall.getResultingDescriptor() !is PropertyDescriptor) return null
val propertyDescriptor = resolvedCall.getResultingDescriptor() as PropertyDescriptor
val propertyDescriptor = resolvedCall.getResultingDescriptor() as PropertyDescriptor
val file = DescriptorToSourceUtils.getContainingFile(propertyDescriptor)
if (file == null) return null
val file = DescriptorToSourceUtils.getContainingFile(propertyDescriptor)
if (file == null) return null
val androidPackage = file.getUserData<String>(AndroidConst.ANDROID_USER_PACKAGE)
if (androidPackage == null) return null
val androidPackage = file.getUserData<String>(AndroidConst.ANDROID_USER_PACKAGE)
if (androidPackage == null) return null
val retType = c.typeMapper.mapType(propertyDescriptor.getReturnType()!!)
receiver.put(Type.getType("Landroid/app/Activity;"), c.v)
c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.getName().asString(), "I")
c.v.invokevirtual("android/app/Activity", "findViewById", "(I)" + "Landroid/view/View;", false)
c.v.checkcast(retType)
val className = DescriptorUtils.getFqName(
resolvedCall.getExtensionReceiver().getType().getConstructor().getDeclarationDescriptor()).toString()
val bytecodeClassName = className.replace('.', '/')
return StackValue.onStack(retType)
val retType = c.typeMapper.mapType(propertyDescriptor.getReturnType()!!)
receiver.put(Type.getType("L$bytecodeClassName;"), c.v)
c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.getName().asString(), "I")
c.v.invokevirtual(bytecodeClassName, methodName, "(I)Landroid/view/View;", false)
c.v.checkcast(retType)
return StackValue.onStack(retType)
}
private fun isClassSupported(descriptor: ClassifierDescriptor): Boolean {
fun classNameSupported(name: String): Boolean = when (name) {
"android.app.Activity" -> true
else -> false
}
if (descriptor is LazyJavaClassDescriptor) {
if (classNameSupported(descriptor.fqName.asString()))
return true
} else if (descriptor is LazyClassDescriptor) { // For tests (FakeActivity)
if (classNameSupported(DescriptorUtils.getFqName(descriptor).toString()))
return true
}
return descriptor.getTypeConstructor().getSupertypes().any {
isClassSupported(it.getConstructor().getDeclarationDescriptor())
}
}
override fun generateClassSyntheticParts(codegen: ClassBuilder, clazz: JetClassOrObject, descriptor: DeclarationDescriptor) {
if (clazz !is JetClass || (clazz.getParent() is JetClassBody) || descriptor !is LazyClassDescriptor) return
// Do not generate anything if class is not supported
if (clazz.isEnum() || clazz.isTrait() || clazz.isAnnotation() || clazz.isInner() || !isClassSupported(descriptor))
return
val className = clazz.getFqName().toString().replace('.', '/')
val classType = Type.getType(className)
val viewType = Type.getType("Landroid/view/View;")
codegen.newField(JvmDeclarationOrigin.NO_ORIGIN, ACC_PRIVATE, propertyName, "Ljava/util/HashMap;", null, null)
val methodVisitor = codegen.newMethod(
JvmDeclarationOrigin.NO_ORIGIN, ACC_PUBLIC, methodName, "(I)Landroid/view/View;", null, null)
methodVisitor.visitCode()
val iv = InstructionAdapter(methodVisitor)
fun getCache() {
iv.load(0, classType)
iv.getfield(className, propertyName, "Ljava/util/HashMap;")
}
fun getId() = iv.load(1, Type.INT_TYPE)
// Get cache property
iv.visitLabel(Label())
getCache()
val lCacheIsNull = Label()
val lCacheNonNull = Label()
iv.ifnonnull(lCacheNonNull)
// Init cache if null
iv.visitLabel(lCacheIsNull)
iv.load(0, classType)
iv.anew(Type.getType("Ljava/util/HashMap;"))
iv.dup()
iv.invokespecial("java/util/HashMap", "<init>", "()V", false)
iv.putfield(className, propertyName, "Ljava/util/HashMap;")
// Get View from cache
iv.visitLabel(lCacheNonNull)
getCache()
getId()
iv.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false)
iv.invokevirtual("java/util/HashMap", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", false)
iv.checkcast(viewType)
iv.store(2, viewType)
val lViewIsNull = Label()
val lViewNonNull = Label()
iv.load(2, viewType)
iv.ifnonnull(lViewNonNull)
// Resolve View via findViewById if not in cache
iv.visitLabel(lViewIsNull)
iv.load(0, classType)
getId()
iv.invokevirtual(className, "findViewById", "(I)Landroid/view/View;", false)
iv.store(2, viewType)
// Store resolved View in cache
getCache()
getId()
iv.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false)
iv.load(2, viewType)
iv.invokevirtual("java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false)
iv.pop()
iv.visitLabel(lViewNonNull)
iv.load(2, viewType)
iv.areturn(viewType)
FunctionCodegen.endVisit(methodVisitor, methodName, clazz)
}
}
public class AndroidComponentRegistrar : ComponentRegistrar {