Android Extensions: Support LayoutContainer in code generation
This commit is contained in:
+6
-10
@@ -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
|
||||
|
||||
+12
-4
@@ -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)
|
||||
|
||||
+10
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+8
-1
@@ -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) }
|
||||
|
||||
+30
-24
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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) }
|
||||
}
|
||||
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user