diff --git a/.idea/artifacts/KotlinPlugin.xml b/.idea/artifacts/KotlinPlugin.xml
index 79ada566ed9..cfd98e040c7 100644
--- a/.idea/artifacts/KotlinPlugin.xml
+++ b/.idea/artifacts/KotlinPlugin.xml
@@ -72,6 +72,7 @@
+
diff --git a/build.xml b/build.xml
index 3040972d801..ed1d3d9ef2e 100644
--- a/build.xml
+++ b/build.xml
@@ -653,9 +653,11 @@
+
+
diff --git a/plugins/android-extensions/android-extensions-compiler/android-extensions-compiler.iml b/plugins/android-extensions/android-extensions-compiler/android-extensions-compiler.iml
index 70f7dd7cc64..a1b321e4270 100644
--- a/plugins/android-extensions/android-extensions-compiler/android-extensions-compiler.iml
+++ b/plugins/android-extensions/android-extensions-compiler/android-extensions-compiler.iml
@@ -13,5 +13,6 @@
+
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidClassType.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidContainerType.kt
similarity index 61%
rename from plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidClassType.kt
rename to plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidContainerType.kt
index e4c27ca0e82..9fd233e82ee 100644
--- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidClassType.kt
+++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidContainerType.kt
@@ -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
}
}
}
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt
index b4348e6b1f9..806468836ad 100644
--- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt
+++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt
@@ -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)
diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidOnDestroyClassBuilderInterceptorExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidOnDestroyClassBuilderInterceptorExtension.kt
index 9216145f7a5..9e190916619 100644
--- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidOnDestroyClassBuilderInterceptorExtension.kt
+++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidOnDestroyClassBuilderInterceptorExtension.kt
@@ -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)
diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/ResourcePropertyStackValue.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/ResourcePropertyStackValue.kt
index c1f9228e602..4e1ef01b65f 100644
--- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/ResourcePropertyStackValue.kt
+++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/ResourcePropertyStackValue.kt
@@ -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")
}
}
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/ContainerOptionsProxy.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/ContainerOptionsProxy.kt
new file mode 100644
index 00000000000..3fa857b4efe
--- /dev/null
+++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/ContainerOptionsProxy.kt
@@ -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 > 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
+ }
+}
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/activityWithEntityOptionsNoCache/activityWithEntityOptionsNoCache.kt b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/activityWithEntityOptionsNoCache/activityWithEntityOptionsNoCache.kt
new file mode 100644
index 00000000000..20698917346
--- /dev/null
+++ b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/activityWithEntityOptionsNoCache/activityWithEntityOptionsNoCache.kt
@@ -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
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/activityWithEntityOptionsNoCache/res/layout/layout.xml b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/activityWithEntityOptionsNoCache/res/layout/layout.xml
new file mode 100644
index 00000000000..4d73173b521
--- /dev/null
+++ b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/activityWithEntityOptionsNoCache/res/layout/layout.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/fragmentWithEntityOptionsNoCache/fragmentWithEntityOptionsNoCache.kt b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/fragmentWithEntityOptionsNoCache/fragmentWithEntityOptionsNoCache.kt
new file mode 100644
index 00000000000..7c54d631ef2
--- /dev/null
+++ b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/fragmentWithEntityOptionsNoCache/fragmentWithEntityOptionsNoCache.kt
@@ -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
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/fragmentWithEntityOptionsNoCache/res/layout/layout.xml b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/fragmentWithEntityOptionsNoCache/res/layout/layout.xml
new file mode 100644
index 00000000000..4d73173b521
--- /dev/null
+++ b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/fragmentWithEntityOptionsNoCache/res/layout/layout.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithCache/viewWithCache.kt b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithCache/viewWithCache.kt
index 3fce6040a40..3b8ed65d4db 100644
--- a/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithCache/viewWithCache.kt
+++ b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithCache/viewWithCache.kt
@@ -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() {
diff --git a/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithDefaultNoCache/res/layout/layout.xml b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithDefaultNoCache/res/layout/layout.xml
new file mode 100644
index 00000000000..4d73173b521
--- /dev/null
+++ b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithDefaultNoCache/res/layout/layout.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithDefaultNoCache/viewWithDefaultNoCache.kt b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithDefaultNoCache/viewWithDefaultNoCache.kt
new file mode 100644
index 00000000000..a653542e11d
--- /dev/null
+++ b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithDefaultNoCache/viewWithDefaultNoCache.kt
@@ -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
diff --git a/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithEntityOptionsNoCache/res/layout/layout.xml b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithEntityOptionsNoCache/res/layout/layout.xml
new file mode 100644
index 00000000000..4d73173b521
--- /dev/null
+++ b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithEntityOptionsNoCache/res/layout/layout.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithEntityOptionsNoCache/viewWithEntityOptionsNoCache.kt b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithEntityOptionsNoCache/viewWithEntityOptionsNoCache.kt
new file mode 100644
index 00000000000..816cdbaae3b
--- /dev/null
+++ b/plugins/android-extensions/android-extensions-compiler/testData/codegen/bytecodeShape/viewWithEntityOptionsNoCache/viewWithEntityOptionsNoCache.kt
@@ -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
diff --git a/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/synthetic/test/AbstractAndroidBytecodeShapeTest.kt b/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/synthetic/test/AbstractAndroidBytecodeShapeTest.kt
index 24ec6d5d7ea..00658d02309 100755
--- a/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/synthetic/test/AbstractAndroidBytecodeShapeTest.kt
+++ b/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/synthetic/test/AbstractAndroidBytecodeShapeTest.kt
@@ -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) {
diff --git a/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/synthetic/test/AndroidBytecodeShapeTestGenerated.java b/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/synthetic/test/AndroidBytecodeShapeTestGenerated.java
index b4991cf7e3b..c6a2b13d639 100644
--- a/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/synthetic/test/AndroidBytecodeShapeTestGenerated.java
+++ b/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/synthetic/test/AndroidBytecodeShapeTestGenerated.java
@@ -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);
+ }
}