Android Extensions: Support @ContainerOptions in compiler plugin
This commit is contained in:
Generated
+1
@@ -72,6 +72,7 @@
|
||||
</element>
|
||||
<element id="archive" name="android-extensions-compiler.jar">
|
||||
<element id="module-output" name="android-extensions-compiler" />
|
||||
<element id="module-output" name="android-extensions-runtime" />
|
||||
</element>
|
||||
<element id="archive" name="android-lint.jar">
|
||||
<element id="module-output" name="uast-kotlin" />
|
||||
|
||||
@@ -653,9 +653,11 @@
|
||||
<skip pattern="kotlin/Metadata"/>
|
||||
<src>
|
||||
<pathelement path="plugins/android-extensions/android-extensions-compiler/src"/>
|
||||
<pathelement path="plugins/android-extensions/android-extensions-runtime/src"/>
|
||||
</src>
|
||||
<classpath>
|
||||
<pathelement path="${idea.sdk}/core/intellij-core.jar"/>
|
||||
<pathelement path="${idea.sdk}/plugins/android/lib/layoutlib.jar"/>
|
||||
<pathelement path="${kotlin-home}/lib/kotlin-compiler.jar"/>
|
||||
</classpath>
|
||||
</javac2>
|
||||
|
||||
@@ -13,5 +13,6 @@
|
||||
<orderEntry type="module" module-name="frontend.java" exported="" />
|
||||
<orderEntry type="module" module-name="util" />
|
||||
<orderEntry type="module" module-name="backend" scope="PROVIDED" />
|
||||
<orderEntry type="module" module-name="android-extensions-runtime" />
|
||||
</component>
|
||||
</module>
|
||||
+18
-18
@@ -22,26 +22,26 @@ 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 AndroidClassType(className: String, val supportsCache: Boolean = false, val fragment: Boolean = false) {
|
||||
ACTIVITY(AndroidConst.ACTIVITY_FQNAME, supportsCache = true),
|
||||
FRAGMENT(AndroidConst.FRAGMENT_FQNAME, supportsCache = true, fragment = true),
|
||||
DIALOG(AndroidConst.DIALOG_FQNAME, supportsCache = false),
|
||||
SUPPORT_FRAGMENT_ACTIVITY(AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME, supportsCache = true),
|
||||
SUPPORT_FRAGMENT(AndroidConst.SUPPORT_FRAGMENT_FQNAME, supportsCache = true, fragment = true),
|
||||
VIEW(AndroidConst.VIEW_FQNAME, supportsCache = true),
|
||||
enum class AndroidContainerType(className: String, val doesSupportCache: Boolean = false, val isFragment: Boolean = false) {
|
||||
ACTIVITY(AndroidConst.ACTIVITY_FQNAME, doesSupportCache = true),
|
||||
FRAGMENT(AndroidConst.FRAGMENT_FQNAME, doesSupportCache = true, isFragment = true),
|
||||
DIALOG(AndroidConst.DIALOG_FQNAME, doesSupportCache = false),
|
||||
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),
|
||||
UNKNOWN("");
|
||||
|
||||
val internalClassName: String = className.replace('.', '/')
|
||||
|
||||
companion object {
|
||||
fun getClassType(descriptor: ClassifierDescriptor): AndroidClassType {
|
||||
fun getClassTypeInternal(name: String): AndroidClassType? = when (name) {
|
||||
AndroidConst.ACTIVITY_FQNAME -> AndroidClassType.ACTIVITY
|
||||
AndroidConst.FRAGMENT_FQNAME -> AndroidClassType.FRAGMENT
|
||||
AndroidConst.DIALOG_FQNAME -> AndroidClassType.DIALOG
|
||||
AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY
|
||||
AndroidConst.SUPPORT_FRAGMENT_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT
|
||||
AndroidConst.VIEW_FQNAME -> AndroidClassType.VIEW
|
||||
fun get(descriptor: ClassifierDescriptor): AndroidContainerType {
|
||||
fun getClassTypeInternal(name: String): AndroidContainerType? = when (name) {
|
||||
AndroidConst.ACTIVITY_FQNAME -> AndroidContainerType.ACTIVITY
|
||||
AndroidConst.FRAGMENT_FQNAME -> AndroidContainerType.FRAGMENT
|
||||
AndroidConst.DIALOG_FQNAME -> AndroidContainerType.DIALOG
|
||||
AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME -> AndroidContainerType.SUPPORT_FRAGMENT_ACTIVITY
|
||||
AndroidConst.SUPPORT_FRAGMENT_FQNAME -> AndroidContainerType.SUPPORT_FRAGMENT
|
||||
AndroidConst.VIEW_FQNAME -> AndroidContainerType.VIEW
|
||||
else -> null
|
||||
}
|
||||
|
||||
@@ -57,12 +57,12 @@ enum class AndroidClassType(className: String, val supportsCache: Boolean = fals
|
||||
for (supertype in descriptor.typeConstructor.supertypes) {
|
||||
val declarationDescriptor = supertype.constructor.declarationDescriptor
|
||||
if (declarationDescriptor != null) {
|
||||
val androidClassType = getClassType(declarationDescriptor)
|
||||
if (androidClassType != AndroidClassType.UNKNOWN) return androidClassType
|
||||
val androidClassType = get(declarationDescriptor)
|
||||
if (androidClassType != AndroidContainerType.UNKNOWN) return androidClassType
|
||||
}
|
||||
}
|
||||
|
||||
return AndroidClassType.UNKNOWN
|
||||
return AndroidContainerType.UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
-32
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.android.synthetic.codegen
|
||||
|
||||
import kotlinx.android.extensions.CacheImplementation.NO_CACHE
|
||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||
import org.jetbrains.kotlin.android.synthetic.descriptors.ContainerOptionsProxy
|
||||
import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticFunction
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticProperty
|
||||
@@ -33,7 +35,6 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PRIVATE
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PUBLIC
|
||||
@@ -48,15 +49,7 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||
val CLEAR_CACHE_METHOD_NAME = "_\$_clearFindViewByIdCache"
|
||||
val ON_DESTROY_METHOD_NAME = "onDestroyView"
|
||||
|
||||
fun doesContainerSupportCache(container: ClassDescriptor): Boolean {
|
||||
return container.source is KotlinSourceElement
|
||||
}
|
||||
|
||||
fun shouldCacheResource(container: ClassDescriptor, resource: PropertyDescriptor): Boolean {
|
||||
if (!doesContainerSupportCache(container)) {
|
||||
return false
|
||||
}
|
||||
|
||||
fun shouldCacheResource(resource: PropertyDescriptor): Boolean {
|
||||
return (resource as? AndroidSyntheticProperty)?.shouldBeCached ?: false
|
||||
}
|
||||
}
|
||||
@@ -66,7 +59,7 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||
val state: GenerationState,
|
||||
val descriptor: ClassDescriptor,
|
||||
val classOrObject: KtClassOrObject,
|
||||
val androidClassType: AndroidClassType)
|
||||
val containerOptions: ContainerOptionsProxy)
|
||||
|
||||
override fun applyProperty(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? {
|
||||
val resultingDescriptor = resolvedCall.resultingDescriptor
|
||||
@@ -94,12 +87,14 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||
container: ClassDescriptor,
|
||||
c: ExpressionCodegenExtension.Context
|
||||
): StackValue? {
|
||||
if (!doesContainerSupportCache(container)) {
|
||||
val containerOptions = ContainerOptionsProxy.get(container)
|
||||
|
||||
if (!containerOptions.cache.hasCache) {
|
||||
return StackValue.functionCall(Type.VOID_TYPE) {}
|
||||
}
|
||||
|
||||
val androidClassType = AndroidClassType.getClassType(container)
|
||||
if (androidClassType == AndroidClassType.UNKNOWN) return null
|
||||
val androidClassType = AndroidContainerType.get(container)
|
||||
if (androidClassType == AndroidContainerType.UNKNOWN) return null
|
||||
|
||||
return StackValue.functionCall(Type.VOID_TYPE) {
|
||||
val bytecodeClassName = c.typeMapper.mapType(container).internalName
|
||||
@@ -113,15 +108,15 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||
receiver: StackValue,
|
||||
resolvedCall: ResolvedCall<*>,
|
||||
c: ExpressionCodegenExtension.Context,
|
||||
descriptor: PropertyDescriptor
|
||||
resource: PropertyDescriptor
|
||||
): StackValue? {
|
||||
if (descriptor !is AndroidSyntheticProperty) return null
|
||||
val packageFragment = descriptor.containingDeclaration as? AndroidSyntheticPackageFragmentDescriptor ?: return null
|
||||
if (resource !is AndroidSyntheticProperty) return null
|
||||
val packageFragment = resource.containingDeclaration as? AndroidSyntheticPackageFragmentDescriptor ?: return null
|
||||
val androidPackage = packageFragment.packageData.moduleData.module.applicationPackage
|
||||
val receiverDescriptor = resolvedCall.getReceiverDeclarationDescriptor() as? ClassDescriptor ?: return null
|
||||
val androidClassType = AndroidClassType.getClassType(receiverDescriptor)
|
||||
val container = resolvedCall.getReceiverDeclarationDescriptor() as? ClassDescriptor ?: return null
|
||||
|
||||
return ResourcePropertyStackValue(receiver, c.typeMapper, descriptor, receiverDescriptor, androidClassType, androidPackage)
|
||||
val containerOptions = ContainerOptionsProxy.get(container)
|
||||
return ResourcePropertyStackValue(receiver, c.typeMapper, resource, container, containerOptions, androidPackage)
|
||||
}
|
||||
|
||||
private fun ResolvedCall<*>.getReceiverDeclarationDescriptor(): ClassifierDescriptor? {
|
||||
@@ -132,19 +127,18 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||
val classBuilder = codegen.v
|
||||
val targetClass = codegen.myClass as? KtClassOrObject ?: return
|
||||
|
||||
val descriptor = codegen.descriptor
|
||||
if (descriptor.kind != ClassKind.CLASS || descriptor.isInner || DescriptorUtils.isLocal(descriptor)) return
|
||||
val container = codegen.descriptor
|
||||
if (container.kind != ClassKind.CLASS || container.isInner || DescriptorUtils.isLocal(container)) return
|
||||
|
||||
// Do not generate anything if class is not supported
|
||||
val androidClassType = AndroidClassType.getClassType(descriptor)
|
||||
if (androidClassType == AndroidClassType.UNKNOWN) return
|
||||
val containerOptions = ContainerOptionsProxy.get(container)
|
||||
if (containerOptions.cache == NO_CACHE) return
|
||||
|
||||
val context = SyntheticPartsGenerateContext(classBuilder, codegen.state, descriptor, targetClass, androidClassType)
|
||||
val context = SyntheticPartsGenerateContext(classBuilder, codegen.state, container, targetClass, containerOptions)
|
||||
context.generateCachedFindViewByIdFunction()
|
||||
context.generateClearCacheFunction()
|
||||
|
||||
if (androidClassType.fragment) {
|
||||
val classMembers = descriptor.unsubstitutedMemberScope.getContributedDescriptors()
|
||||
if (containerOptions.classType.isFragment) {
|
||||
val classMembers = container.unsubstitutedMemberScope.getContributedDescriptors()
|
||||
val onDestroy = classMembers.firstOrNull { it is FunctionDescriptor && it.isOnDestroyFunction() }
|
||||
if (onDestroy == null) {
|
||||
context.generateOnDestroyFunctionForFragment()
|
||||
@@ -249,12 +243,12 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||
|
||||
// Resolve View via findViewById if not in cache
|
||||
iv.load(0, classType)
|
||||
when (androidClassType) {
|
||||
AndroidClassType.ACTIVITY, AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY, AndroidClassType.VIEW, AndroidClassType.DIALOG -> {
|
||||
when (containerOptions.classType) {
|
||||
AndroidContainerType.ACTIVITY, AndroidContainerType.SUPPORT_FRAGMENT_ACTIVITY, AndroidContainerType.VIEW, AndroidContainerType.DIALOG -> {
|
||||
loadId()
|
||||
iv.invokevirtual(className, "findViewById", "(I)Landroid/view/View;", false)
|
||||
}
|
||||
AndroidClassType.FRAGMENT, AndroidClassType.SUPPORT_FRAGMENT -> {
|
||||
AndroidContainerType.FRAGMENT, AndroidContainerType.SUPPORT_FRAGMENT -> {
|
||||
iv.invokevirtual(className, "getView", "()Landroid/view/View;", false)
|
||||
iv.dup()
|
||||
val lgetViewNotNull = Label()
|
||||
@@ -270,7 +264,7 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||
loadId()
|
||||
iv.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false)
|
||||
}
|
||||
else -> throw IllegalStateException("Can't generate code for $androidClassType")
|
||||
else -> throw IllegalStateException("Can't generate code for ${containerOptions.classType}")
|
||||
}
|
||||
iv.store(2, viewType)
|
||||
|
||||
|
||||
+5
-3
@@ -17,6 +17,8 @@
|
||||
package org.jetbrains.kotlin.android.synthetic.codegen
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import kotlinx.android.extensions.CacheImplementation.NO_CACHE
|
||||
import org.jetbrains.kotlin.android.synthetic.descriptors.ContainerOptionsProxy
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactory
|
||||
import org.jetbrains.kotlin.codegen.DelegatingClassBuilder
|
||||
@@ -111,9 +113,9 @@ class AndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptor
|
||||
|
||||
val classType = currentClassName?.let { Type.getObjectType(it) } ?: return
|
||||
|
||||
val descriptor = bindingContext.get(BindingContext.CLASS, currentClass) ?: return
|
||||
val androidClassType = AndroidClassType.getClassType(descriptor)
|
||||
if (!androidClassType.fragment) return
|
||||
val container = bindingContext.get(BindingContext.CLASS, currentClass) ?: return
|
||||
val containerOptions = ContainerOptionsProxy.get(container)
|
||||
if (!containerOptions.classType.isFragment || containerOptions.cache == NO_CACHE) return
|
||||
|
||||
val iv = InstructionAdapter(this)
|
||||
iv.load(0, classType)
|
||||
|
||||
+17
-14
@@ -17,6 +17,8 @@
|
||||
package org.jetbrains.kotlin.android.synthetic.codegen
|
||||
|
||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension.Companion.shouldCacheResource
|
||||
import org.jetbrains.kotlin.android.synthetic.descriptors.ContainerOptionsProxy
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticProperty
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
@@ -29,22 +31,23 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
class ResourcePropertyStackValue(
|
||||
val receiver: StackValue,
|
||||
val typeMapper: KotlinTypeMapper,
|
||||
val propertyDescriptor: PropertyDescriptor,
|
||||
val receiverDescriptor: ClassDescriptor,
|
||||
val androidClassType: AndroidClassType,
|
||||
val resource: PropertyDescriptor,
|
||||
val container: ClassDescriptor,
|
||||
val containerOptions: ContainerOptionsProxy,
|
||||
val androidPackage: String
|
||||
) : StackValue(typeMapper.mapType(propertyDescriptor.returnType!!)) {
|
||||
) : StackValue(typeMapper.mapType(resource.returnType!!)) {
|
||||
private val androidClassType get() = containerOptions.classType
|
||||
|
||||
override fun putSelector(type: Type, v: InstructionAdapter) {
|
||||
val returnTypeString = typeMapper.mapType(propertyDescriptor.type.lowerIfFlexible()).className
|
||||
val returnTypeString = typeMapper.mapType(resource.type.lowerIfFlexible()).className
|
||||
if (AndroidConst.FRAGMENT_FQNAME == returnTypeString || AndroidConst.SUPPORT_FRAGMENT_FQNAME == returnTypeString) {
|
||||
return putSelectorForFragment(v)
|
||||
}
|
||||
|
||||
val syntheticProperty = propertyDescriptor as AndroidSyntheticProperty
|
||||
val syntheticProperty = resource as AndroidSyntheticProperty
|
||||
|
||||
if (androidClassType.supportsCache && AndroidExpressionCodegenExtension.shouldCacheResource(receiverDescriptor, propertyDescriptor)) {
|
||||
val declarationDescriptorType = typeMapper.mapType(receiverDescriptor)
|
||||
if (containerOptions.cache.hasCache && shouldCacheResource(resource)) {
|
||||
val declarationDescriptorType = typeMapper.mapType(container)
|
||||
receiver.put(declarationDescriptorType, v)
|
||||
|
||||
val resourceId = syntheticProperty.resource.id
|
||||
@@ -55,12 +58,12 @@ class ResourcePropertyStackValue(
|
||||
}
|
||||
else {
|
||||
when (androidClassType) {
|
||||
AndroidClassType.ACTIVITY, AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY, AndroidClassType.VIEW, AndroidClassType.DIALOG -> {
|
||||
AndroidContainerType.ACTIVITY, AndroidContainerType.SUPPORT_FRAGMENT_ACTIVITY, AndroidContainerType.VIEW, AndroidContainerType.DIALOG -> {
|
||||
receiver.put(Type.getType("L${androidClassType.internalClassName};"), v)
|
||||
getResourceId(v)
|
||||
v.invokevirtual(androidClassType.internalClassName, "findViewById", "(I)Landroid/view/View;", false)
|
||||
}
|
||||
AndroidClassType.FRAGMENT, AndroidClassType.SUPPORT_FRAGMENT -> {
|
||||
AndroidContainerType.FRAGMENT, AndroidContainerType.SUPPORT_FRAGMENT -> {
|
||||
receiver.put(Type.getType("L${androidClassType.internalClassName};"), v)
|
||||
v.invokevirtual(androidClassType.internalClassName, "getView", "()Landroid/view/View;", false)
|
||||
getResourceId(v)
|
||||
@@ -77,17 +80,17 @@ class ResourcePropertyStackValue(
|
||||
receiver.put(Type.getType("L${androidClassType.internalClassName};"), v)
|
||||
|
||||
when (androidClassType) {
|
||||
AndroidClassType.ACTIVITY, AndroidClassType.FRAGMENT -> {
|
||||
AndroidContainerType.ACTIVITY, AndroidContainerType.FRAGMENT -> {
|
||||
v.invokevirtual(androidClassType.internalClassName, "getFragmentManager", "()Landroid/app/FragmentManager;", false)
|
||||
getResourceId(v)
|
||||
v.invokevirtual("android/app/FragmentManager", "findFragmentById", "(I)Landroid/app/Fragment;", false)
|
||||
}
|
||||
AndroidClassType.SUPPORT_FRAGMENT -> {
|
||||
AndroidContainerType.SUPPORT_FRAGMENT -> {
|
||||
v.invokevirtual(androidClassType.internalClassName, "getFragmentManager", "()Landroid/support/v4/app/FragmentManager;", false)
|
||||
getResourceId(v)
|
||||
v.invokevirtual("android/support/v4/app/FragmentManager", "findFragmentById", "(I)Landroid/support/v4/app/Fragment;", false)
|
||||
}
|
||||
AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY -> {
|
||||
AndroidContainerType.SUPPORT_FRAGMENT_ACTIVITY -> {
|
||||
v.invokevirtual(androidClassType.internalClassName, "getSupportFragmentManager", "()Landroid/support/v4/app/FragmentManager;", false)
|
||||
getResourceId(v)
|
||||
v.invokevirtual("android/support/v4/app/FragmentManager", "findFragmentById", "(I)Landroid/support/v4/app/Fragment;", false)
|
||||
@@ -99,6 +102,6 @@ class ResourcePropertyStackValue(
|
||||
}
|
||||
|
||||
fun getResourceId(v: InstructionAdapter) {
|
||||
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.name.asString(), "I")
|
||||
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", resource.name.asString(), "I")
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.synthetic.descriptors
|
||||
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||
import kotlinx.android.extensions.CacheImplementation
|
||||
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.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.constants.EnumValue
|
||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||
|
||||
class ContainerOptionsProxy(val classType: AndroidContainerType, val cache: CacheImplementation) {
|
||||
companion object {
|
||||
private val CONTAINER_OPTIONS_FQNAME = FqName(ContainerOptions::class.java.canonicalName)
|
||||
private val CACHE_NAME = ContainerOptions::cache.name
|
||||
|
||||
private val DEFAULT_CACHE_IMPL = HASH_MAP
|
||||
|
||||
fun get(container: ClassDescriptor): ContainerOptionsProxy {
|
||||
val classType = AndroidContainerType.get(container)
|
||||
|
||||
val anno = container.annotations.findAnnotation(CONTAINER_OPTIONS_FQNAME)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
val cache = anno.getEnumValue(CACHE_NAME, HASH_MAP) { valueOf(it) }
|
||||
|
||||
return ContainerOptionsProxy(classType, cache)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private operator fun AnnotationDescriptor.get(name: String): ConstantValue<*>? {
|
||||
return allValueArguments.entries.firstOrNull { it.key.name.asString() == name }?.value
|
||||
}
|
||||
|
||||
private fun <E: Enum<E>> AnnotationDescriptor.getEnumValue(name: String, defaultValue: E, factory: (String) -> E): E {
|
||||
val valueName = (this[name] as? EnumValue)?.value?.name?.asString() ?: defaultValue.name
|
||||
|
||||
return try {
|
||||
factory(valueName)
|
||||
} catch (e: Exception) {
|
||||
defaultValue
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package test
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import java.io.File
|
||||
import kotlinx.android.synthetic.main.layout.*
|
||||
import kotlinx.android.extensions.*
|
||||
|
||||
@ContainerOptions(cache = CacheImplementation.NO_CACHE)
|
||||
public class MyActivity : Activity() {
|
||||
init {login}
|
||||
}
|
||||
|
||||
// 0 public _\$_findCachedViewById
|
||||
// 0 public _\$_clearFindViewByIdCache
|
||||
// 1 GETSTATIC test/R\$id\.login
|
||||
// 0 INVOKEVIRTUAL test/MyActivity\._\$_findCachedViewById
|
||||
// 1 INVOKEVIRTUAL android/app/Activity\.findViewById
|
||||
// 1 CHECKCAST android/widget/Button
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/item_detail_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ItemDetailActivity"
|
||||
tools:ignore="MergeRootFrame" >
|
||||
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/login"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Sign in" />
|
||||
|
||||
</FrameLayout>
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package test
|
||||
|
||||
import android.app.Fragment
|
||||
import java.io.File
|
||||
import kotlinx.android.synthetic.main.layout.*
|
||||
import kotlinx.android.extensions.*
|
||||
|
||||
@ContainerOptions(cache = CacheImplementation.NO_CACHE)
|
||||
public class MyFragment : Fragment() {
|
||||
init {login}
|
||||
}
|
||||
|
||||
// 0 public _\$_findCachedViewById
|
||||
// 0 public _\$_clearFindViewByIdCache
|
||||
// 1 INVOKEVIRTUAL android/app/Fragment\.getView
|
||||
// 1 GETSTATIC test/R\$id\.login
|
||||
// 0 INVOKEVIRTUAL test/MyFragment\._\$_findCachedViewById
|
||||
// 1 INVOKEVIRTUAL android/app/Fragment\.getView
|
||||
// 1 INVOKEVIRTUAL android/view/View\.findViewById
|
||||
// 1 CHECKCAST android/widget/Button
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/item_detail_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ItemDetailActivity"
|
||||
tools:ignore="MergeRootFrame" >
|
||||
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/login"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Sign in" />
|
||||
|
||||
</FrameLayout>
|
||||
+2
@@ -4,7 +4,9 @@ import android.view.View
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import kotlinx.android.synthetic.main.layout.view.*
|
||||
import kotlinx.android.extensions.*
|
||||
|
||||
@ContainerOptions(cache = CacheImplementation.HASH_MAP)
|
||||
class MyView(context: Context) : View(context)
|
||||
|
||||
class MyActivity : Activity() {
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/item_detail_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ItemDetailActivity"
|
||||
tools:ignore="MergeRootFrame" >
|
||||
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/login"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Sign in" />
|
||||
|
||||
</FrameLayout>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package test
|
||||
|
||||
import android.view.View
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import kotlinx.android.synthetic.main.layout.view.*
|
||||
|
||||
class MyView(context: Context) : View(context)
|
||||
|
||||
class MyActivity : Activity() {
|
||||
init { MyView(this).login }
|
||||
}
|
||||
|
||||
// 2 public _\$_findCachedViewById
|
||||
// 1 INVOKEVIRTUAL android/app/Activity\.findViewById
|
||||
// 2 public _\$_clearFindViewByIdCache
|
||||
// 1 GETSTATIC test/R\$id\.login
|
||||
// 1 INVOKEVIRTUAL android/view/View\.findViewById
|
||||
// 0 INVOKEVIRTUAL test/MyActivity\._\$_findCachedViewById
|
||||
// 0 INVOKEVIRTUAL android/view/View\._\$_findCachedViewById
|
||||
// 1 CHECKCAST android/widget/Button
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/item_detail_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ItemDetailActivity"
|
||||
tools:ignore="MergeRootFrame" >
|
||||
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/login"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Sign in" />
|
||||
|
||||
</FrameLayout>
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package test
|
||||
|
||||
import android.view.View
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import kotlinx.android.synthetic.main.layout.view.*
|
||||
import kotlinx.android.extensions.*
|
||||
|
||||
@ContainerOptions(cache = CacheImplementation.NO_CACHE)
|
||||
class MyView(context: Context) : View(context)
|
||||
|
||||
class MyActivity : Activity() {
|
||||
init { MyView(this).login }
|
||||
}
|
||||
|
||||
// 1 public _\$_findCachedViewById
|
||||
// 1 INVOKEVIRTUAL test/MyActivity\.findViewById
|
||||
// 1 public _\$_clearFindViewByIdCache
|
||||
// 1 GETSTATIC test/R\$id\.login
|
||||
// 1 INVOKEVIRTUAL android/view/View\.findViewById
|
||||
// 0 INVOKEVIRTUAL test/MyView\.findViewById
|
||||
// 0 INVOKEVIRTUAL test/MyActivity\._\$_findCachedViewById
|
||||
// 0 INVOKEVIRTUAL android/view/View\._\$_findCachedViewById
|
||||
// 1 CHECKCAST android/widget/Button
|
||||
+6
-1
@@ -16,11 +16,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.android.synthetic.test
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||
import org.jetbrains.kotlin.codegen.AbstractBytecodeTextTest
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractAndroidBytecodeShapeTest : AbstractBytecodeTextTest() {
|
||||
|
||||
@@ -30,7 +32,10 @@ abstract class AbstractAndroidBytecodeShapeTest : AbstractBytecodeTextTest() {
|
||||
|
||||
private fun createEnvironmentForConfiguration(configuration: CompilerConfiguration, path: String) {
|
||||
val layoutPaths = getResPaths(path)
|
||||
myEnvironment = createTestEnvironment(configuration, layoutPaths)
|
||||
myEnvironment = createTestEnvironment(configuration, layoutPaths).apply {
|
||||
val runtimeLibrary = File("out/production/android-extensions-runtime")
|
||||
updateClasspath(listOf(JvmClasspathRoot(runtimeLibrary)))
|
||||
}
|
||||
}
|
||||
|
||||
override fun doTest(path: String) {
|
||||
|
||||
+24
@@ -32,6 +32,12 @@ import java.util.regex.Pattern;
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class AndroidBytecodeShapeTestGenerated extends AbstractAndroidBytecodeShapeTest {
|
||||
@TestMetadata("activityWithEntityOptionsNoCache")
|
||||
public void testActivityWithEntityOptionsNoCache() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/activityWithEntityOptionsNoCache/");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInBytecodeShape() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, false);
|
||||
}
|
||||
@@ -108,6 +114,12 @@ public class AndroidBytecodeShapeTestGenerated extends AbstractAndroidBytecodeSh
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fragmentWithEntityOptionsNoCache")
|
||||
public void testFragmentWithEntityOptionsNoCache() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/fragmentWithEntityOptionsNoCache/");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multiFile")
|
||||
public void testMultiFile() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/multiFile/");
|
||||
@@ -179,4 +191,16 @@ public class AndroidBytecodeShapeTestGenerated extends AbstractAndroidBytecodeSh
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithCache/");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("viewWithDefaultNoCache")
|
||||
public void testViewWithDefaultNoCache() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithDefaultNoCache/");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("viewWithEntityOptionsNoCache")
|
||||
public void testViewWithEntityOptionsNoCache() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithEntityOptionsNoCache/");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user