Android Extensions: Support LayoutContainer in code generation

This commit is contained in:
Yan Zhulanow
2017-06-05 22:46:37 +03:00
parent a69f9729e0
commit 423a09e46a
32 changed files with 279 additions and 56 deletions
@@ -16,11 +16,10 @@
package org.jetbrains.kotlin.android.synthetic.codegen
import kotlinx.android.extensions.LayoutContainer
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
enum class AndroidContainerType(className: String, val doesSupportCache: Boolean = false, val isFragment: Boolean = false) {
ACTIVITY(AndroidConst.ACTIVITY_FQNAME, doesSupportCache = true),
@@ -29,11 +28,14 @@ enum class AndroidContainerType(className: String, val doesSupportCache: Boolean
SUPPORT_FRAGMENT_ACTIVITY(AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME, doesSupportCache = true),
SUPPORT_FRAGMENT(AndroidConst.SUPPORT_FRAGMENT_FQNAME, doesSupportCache = true, isFragment = true),
VIEW(AndroidConst.VIEW_FQNAME, doesSupportCache = true),
LAYOUT_CONTAINER(LayoutContainer::class.java.canonicalName, doesSupportCache = true),
UNKNOWN("");
val internalClassName: String = className.replace('.', '/')
companion object {
private val LAYOUT_CONTAINER_FQNAME = LayoutContainer::class.java.canonicalName
fun get(descriptor: ClassifierDescriptor): AndroidContainerType {
fun getClassTypeInternal(name: String): AndroidContainerType? = when (name) {
AndroidConst.ACTIVITY_FQNAME -> AndroidContainerType.ACTIVITY
@@ -42,17 +44,11 @@ enum class AndroidContainerType(className: String, val doesSupportCache: Boolean
AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME -> AndroidContainerType.SUPPORT_FRAGMENT_ACTIVITY
AndroidConst.SUPPORT_FRAGMENT_FQNAME -> AndroidContainerType.SUPPORT_FRAGMENT
AndroidConst.VIEW_FQNAME -> AndroidContainerType.VIEW
LAYOUT_CONTAINER_FQNAME -> AndroidContainerType.LAYOUT_CONTAINER
else -> null
}
if (descriptor is LazyJavaClassDescriptor) {
val androidClassType = getClassTypeInternal(DescriptorUtils.getFqName(descriptor).asString())
if (androidClassType != null) return androidClassType
}
else if (descriptor is LazyClassDescriptor) { // For tests (FakeActivity)
val androidClassType = getClassTypeInternal(DescriptorUtils.getFqName(descriptor).toString())
if (androidClassType != null) return androidClassType
}
getClassTypeInternal(DescriptorUtils.getFqName(descriptor).asString())?.let { return it }
for (supertype in descriptor.typeConstructor.supertypes) {
val declarationDescriptor = supertype.constructor.declarationDescriptor
@@ -93,8 +93,7 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
return StackValue.functionCall(Type.VOID_TYPE) {}
}
val androidClassType = AndroidContainerType.get(container)
if (androidClassType == AndroidContainerType.UNKNOWN) return null
if (containerOptions.classType == AndroidContainerType.UNKNOWN) return null
return StackValue.functionCall(Type.VOID_TYPE) {
val bytecodeClassName = c.typeMapper.mapType(container).internalName
@@ -248,8 +247,17 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
loadId()
iv.invokevirtual(containerOptions.classType.internalClassName, "findViewById", "(I)Landroid/view/View;", false)
}
AndroidContainerType.FRAGMENT, AndroidContainerType.SUPPORT_FRAGMENT -> {
iv.invokevirtual(containerOptions.classType.internalClassName, "getView", "()Landroid/view/View;", false)
AndroidContainerType.FRAGMENT, AndroidContainerType.SUPPORT_FRAGMENT, AndroidContainerType.LAYOUT_CONTAINER -> {
val methodName: String
val targetClassName: String
if (containerOptions.classType == AndroidContainerType.LAYOUT_CONTAINER) {
methodName = "getEntityView"
targetClassName = classType.internalName
} else {
methodName = "getView"
targetClassName = containerOptions.classType.internalClassName
}
iv.invokevirtual(targetClassName, methodName, "()Landroid/view/View;", false)
iv.dup()
val lgetViewNotNull = Label()
iv.ifnonnull(lgetViewNotNull)
@@ -38,6 +38,10 @@ class ResourcePropertyStackValue(
) : StackValue(typeMapper.mapType(resource.returnType!!)) {
private val androidClassType get() = containerOptions.classType
init {
assert(containerOptions.classType != AndroidContainerType.UNKNOWN)
}
override fun putSelector(type: Type, v: InstructionAdapter) {
val returnTypeString = typeMapper.mapType(resource.type.lowerIfFlexible()).className
if (AndroidConst.FRAGMENT_FQNAME == returnTypeString || AndroidConst.SUPPORT_FRAGMENT_FQNAME == returnTypeString) {
@@ -69,6 +73,12 @@ class ResourcePropertyStackValue(
getResourceId(v)
v.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false)
}
AndroidContainerType.LAYOUT_CONTAINER -> {
receiver.put(Type.getType("L${androidClassType.internalClassName};"), v)
v.invokevirtual(androidClassType.internalClassName, "getEntityView", "()Landroid/view/View;", false)
getResourceId(v)
v.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false)
}
else -> throw IllegalStateException("Invalid Android class type: $androidClassType") // Should never occur
}
}
@@ -22,6 +22,7 @@ import kotlinx.android.extensions.CacheImplementation.*
import kotlinx.android.extensions.ContainerOptions
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidContainerType
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.constants.EnumValue
@@ -35,6 +36,10 @@ class ContainerOptionsProxy(val classType: AndroidContainerType, val cache: Cach
private val DEFAULT_CACHE_IMPL = HASH_MAP
fun get(container: ClassDescriptor): ContainerOptionsProxy {
if (container.kind != ClassKind.CLASS) {
return ContainerOptionsProxy(AndroidContainerType.UNKNOWN, NO_CACHE)
}
val classType = AndroidContainerType.get(container)
val anno = container.annotations.findAnnotation(CONTAINER_OPTIONS_FQNAME)
@@ -42,7 +47,9 @@ class ContainerOptionsProxy(val classType: AndroidContainerType, val cache: Cach
if (anno == null) {
// Java classes (and Kotlin classes from other modules) does not support cache by default
val supportsCache = container.source is KotlinSourceElement && classType.doesSupportCache
return ContainerOptionsProxy(classType, if (supportsCache) DEFAULT_CACHE_IMPL else NO_CACHE)
return ContainerOptionsProxy(
classType,
if (supportsCache) DEFAULT_CACHE_IMPL else NO_CACHE)
}
val cache = anno.getEnumValue(CACHE_NAME, HASH_MAP) { valueOf(it) }
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.android.synthetic.descriptors
import kotlinx.android.extensions.LayoutContainer
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
@@ -43,56 +44,61 @@ class LazySyntheticElementResolveContext(private val module: ModuleDescriptor, s
val dialogDescriptor = find(AndroidConst.DIALOG_FQNAME) ?: return SyntheticElementResolveContext.ERROR_CONTEXT
val supportActivityDescriptor = find(AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME)
val supportFragmentDescriptor = find(AndroidConst.SUPPORT_FRAGMENT_FQNAME)
val layoutContainerDescriptor = find(LayoutContainer::class.java.canonicalName)
return SyntheticElementResolveContext(
viewDescriptor.defaultType,
activityDescriptor.defaultType,
fragmentDescriptor?.defaultType,
dialogDescriptor.defaultType,
supportActivityDescriptor?.defaultType,
supportFragmentDescriptor?.defaultType)
view = viewDescriptor.defaultType,
activity = activityDescriptor.defaultType,
fragment = fragmentDescriptor?.defaultType,
dialog = dialogDescriptor.defaultType,
supportActivity = supportActivityDescriptor?.defaultType,
supportFragment = supportFragmentDescriptor?.defaultType,
layoutContainer = layoutContainerDescriptor?.defaultType)
}
}
internal class SyntheticElementResolveContext(
val viewType: SimpleType,
val activityType: SimpleType,
val fragmentType: SimpleType?,
val dialogType: SimpleType,
val supportActivityType: SimpleType?,
val supportFragmentType: SimpleType?) {
val view: SimpleType,
val activity: SimpleType,
val fragment: SimpleType?,
val dialog: SimpleType,
val supportActivity: SimpleType?,
val supportFragment: SimpleType?,
val layoutContainer: SimpleType?
) {
companion object {
private fun errorType() = ErrorUtils.createErrorType("")
val ERROR_CONTEXT = SyntheticElementResolveContext(errorType(), errorType(), null, errorType(), null, null)
val ERROR_CONTEXT = SyntheticElementResolveContext(errorType(), errorType(), null, errorType(), null, null, null)
}
private val widgetReceivers by lazy {
val receivers = ArrayList<WidgetReceiver>(4)
receivers += WidgetReceiver(activityType, mayHaveCache = true)
receivers += WidgetReceiver(dialogType, mayHaveCache = false)
fragmentType?.let { receivers += WidgetReceiver(it, mayHaveCache = true) }
supportFragmentType?.let { receivers += WidgetReceiver(it, mayHaveCache = true) }
receivers += WidgetReceiver(activity, mayHaveCache = true)
receivers += WidgetReceiver(dialog, mayHaveCache = false)
fragment?.let { receivers += WidgetReceiver(it, mayHaveCache = true) }
supportFragment?.let { receivers += WidgetReceiver(it, mayHaveCache = true) }
layoutContainer?.let { receivers += WidgetReceiver(it, mayHaveCache = true) }
receivers
}
val fragmentTypes: List<Pair<SimpleType, SimpleType>> by lazy {
if (fragmentType == null) {
if (fragment == null) {
emptyList<Pair<SimpleType, SimpleType>>()
}
else {
val types = ArrayList<Pair<SimpleType, SimpleType>>(4)
types += Pair(activityType, fragmentType)
types += Pair(fragmentType, fragmentType)
if (supportActivityType != null && supportFragmentType != null) {
types += Pair(supportFragmentType, supportFragmentType)
types += Pair(supportActivityType, supportFragmentType)
types += Pair(activity, fragment)
types += Pair(fragment, fragment)
if (supportActivity != null && supportFragment != null) {
types += Pair(supportFragment, supportFragment)
types += Pair(supportActivity, supportFragment)
}
types
}
}
fun getWidgetReceivers(forView: Boolean): List<WidgetReceiver> {
if (forView) return listOf(WidgetReceiver(viewType, mayHaveCache = false))
if (forView) return listOf(WidgetReceiver(view, mayHaveCache = true))
return widgetReceivers
}
@@ -79,7 +79,7 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider
// Package with clearFindViewByIdCache()
AndroidConst.SYNTHETIC_SUBPACKAGES.last().let { s ->
val packageDescriptor = PredefinedPackageFragmentDescriptor(s, module, storageManager, packagesToLookupInCompletion) { descriptor ->
lazyContext().getWidgetReceivers(false)
(lazyContext().getWidgetReceivers(false) + lazyContext().getWidgetReceivers(true))
.filter { it.mayHaveCache }
.map { genClearCacheFunction(descriptor, it.type) }
}
@@ -69,7 +69,7 @@ internal fun genPropertyForWidget(
KotlinTypeFactory.simpleNotNullType(
Annotations.EMPTY, classDescriptor, defaultType.constructor.parameters.map(::StarProjectionImpl))
}
} ?: context.viewType
} ?: context.view
return genProperty(resolvedWidget.widget, receiverType, type, packageFragmentDescriptor, sourceEl, resolvedWidget.errorType)
}