diff --git a/idea/src/META-INF/android.xml b/idea/src/META-INF/android.xml
index 46b3918e194..01e6be4bf4f 100644
--- a/idea/src/META-INF/android.xml
+++ b/idea/src/META-INF/android.xml
@@ -74,7 +74,7 @@
-
+
diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt
index f7573581f47..4e3fbec660b 100644
--- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt
+++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt
@@ -19,11 +19,12 @@ package org.jetbrains.kotlin.android.synthetic
import com.intellij.mock.MockProject
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.project.Project
+import kotlinx.android.extensions.CacheImplementation
import org.jetbrains.kotlin.android.parcel.ParcelableCodegenExtension
import org.jetbrains.kotlin.android.parcel.ParcelableDeclarationChecker
import org.jetbrains.kotlin.android.parcel.ParcelableResolveExtension
-import org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidExtensionsExpressionCodegenExtension
+import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidOnDestroyClassBuilderInterceptorExtension
import org.jetbrains.kotlin.android.synthetic.diagnostic.AndroidExtensionPropertiesCallChecker
import org.jetbrains.kotlin.android.synthetic.diagnostic.DefaultErrorMessagesAndroid
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
@@ -49,18 +50,21 @@ import org.jetbrains.kotlin.android.synthetic.codegen.ParcelableClinitClassBuild
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
object AndroidConfigurationKeys {
- val VARIANT: CompilerConfigurationKey> = CompilerConfigurationKey.create>("Android build variant")
- val PACKAGE: CompilerConfigurationKey = CompilerConfigurationKey.create("application package fq name")
- val EXPERIMENTAL: CompilerConfigurationKey = CompilerConfigurationKey.create("enable experimental features")
+ val VARIANT = CompilerConfigurationKey.create>("Android build variant")
+ val PACKAGE = CompilerConfigurationKey.create("application package fq name")
+ val EXPERIMENTAL = CompilerConfigurationKey.create("enable experimental features")
+ val DEFAULT_CACHE_IMPL = CompilerConfigurationKey.create("default cache implementation")
}
class AndroidCommandLineProcessor : CommandLineProcessor {
companion object {
val ANDROID_COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.android"
- val VARIANT_OPTION: CliOption = CliOption("variant", "", "Android build variant", allowMultipleOccurrences = true)
- val PACKAGE_OPTION: CliOption = CliOption("package", "", "Application package")
- val EXPERIMENTAL_OPTION: CliOption = CliOption("experimental", "true/false", "Enable experimental features", required = false)
+ val VARIANT_OPTION = CliOption("variant", "", "Android build variant", allowMultipleOccurrences = true)
+ val PACKAGE_OPTION = CliOption("package", "", "Application package")
+ val EXPERIMENTAL_OPTION = CliOption("experimental", "true/false", "Enable experimental features", required = false)
+ val DEFAULT_CACHE_IMPL_OPTION = CliOption(
+ "defaultCacheImplementation", "hashMap/sparseArray/none", "Default cache implementation for module", required = false)
/* This option is just for saving Android Extensions status in Kotlin facet. It should not be supported from CLI. */
val ENABLED_OPTION: CliOption = CliOption("enabled", "true/false", "Enable Android Extensions", required = false)
@@ -68,13 +72,15 @@ class AndroidCommandLineProcessor : CommandLineProcessor {
override val pluginId: String = ANDROID_COMPILER_PLUGIN_ID
- override val pluginOptions: Collection = listOf(VARIANT_OPTION, PACKAGE_OPTION, EXPERIMENTAL_OPTION)
+ override val pluginOptions: Collection
+ = listOf(VARIANT_OPTION, PACKAGE_OPTION, EXPERIMENTAL_OPTION, DEFAULT_CACHE_IMPL_OPTION)
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) {
when (option) {
VARIANT_OPTION -> configuration.appendList(AndroidConfigurationKeys.VARIANT, value)
PACKAGE_OPTION -> configuration.put(AndroidConfigurationKeys.PACKAGE, value)
EXPERIMENTAL_OPTION -> configuration.put(AndroidConfigurationKeys.EXPERIMENTAL, value)
+ DEFAULT_CACHE_IMPL_OPTION -> configuration.put(AndroidConfigurationKeys.DEFAULT_CACHE_IMPL, value)
else -> throw CliOptionProcessingException("Unknown option: ${option.name}")
}
}
@@ -87,12 +93,19 @@ class AndroidComponentRegistrar : ComponentRegistrar {
SyntheticResolveExtension.registerExtension(project, ParcelableResolveExtension())
ClassBuilderInterceptorExtension.registerExtension(project, ParcelableClinitClassBuilderInterceptorExtension())
}
+
+ fun parseCacheImplementationType(s: String?): CacheImplementation = when (s) {
+ "sparseArray" -> CacheImplementation.SPARSE_ARRAY
+ "none" -> CacheImplementation.NO_CACHE
+ else -> CacheImplementation.DEFAULT
+ }
}
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
val applicationPackage = configuration.get(AndroidConfigurationKeys.PACKAGE)
val variants = configuration.get(AndroidConfigurationKeys.VARIANT)?.mapNotNull { parseVariant(it) } ?: emptyList()
val isExperimental = configuration.get(AndroidConfigurationKeys.EXPERIMENTAL) == "true"
+ val globalCacheImpl = parseCacheImplementationType(configuration.get(AndroidConfigurationKeys.DEFAULT_CACHE_IMPL))
if (isExperimental) {
registerParcelExtensions(project)
@@ -102,10 +115,10 @@ class AndroidComponentRegistrar : ComponentRegistrar {
val layoutXmlFileManager = CliAndroidLayoutXmlFileManager(project, applicationPackage!!, variants)
project.registerService(AndroidLayoutXmlFileManager::class.java, layoutXmlFileManager)
- ExpressionCodegenExtension.registerExtension(project, CliAndroidExtensionsExpressionCodegenExtension(isExperimental))
+ ExpressionCodegenExtension.registerExtension(project, CliAndroidExtensionsExpressionCodegenExtension(isExperimental, globalCacheImpl))
StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor())
Extensions.getRootArea().getExtensionPoint(DefaultErrorMessages.Extension.EP_NAME).registerExtension(DefaultErrorMessagesAndroid())
- ClassBuilderInterceptorExtension.registerExtension(project, AndroidOnDestroyClassBuilderInterceptorExtension())
+ ClassBuilderInterceptorExtension.registerExtension(project, CliAndroidOnDestroyClassBuilderInterceptorExtension(globalCacheImpl))
PackageFragmentProviderExtension.registerExtension(project, CliAndroidPackageFragmentProviderExtension(isExperimental))
}
}
diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AbstractAndroidExtensionsExpressionCodegenExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AbstractAndroidExtensionsExpressionCodegenExtension.kt
index 446ab4daeb4..a665ae8fda9 100644
--- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AbstractAndroidExtensionsExpressionCodegenExtension.kt
+++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AbstractAndroidExtensionsExpressionCodegenExtension.kt
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.android.synthetic.codegen
+import kotlinx.android.extensions.CacheImplementation
import kotlinx.android.extensions.CacheImplementation.NO_CACHE
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidContainerType.LAYOUT_CONTAINER
@@ -32,6 +33,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
@@ -51,9 +53,7 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
val CLEAR_CACHE_METHOD_NAME = "_\$_clearFindViewByIdCache"
val ON_DESTROY_METHOD_NAME = "onDestroyView"
- fun shouldCacheResource(resource: PropertyDescriptor): Boolean {
- return (resource as? AndroidSyntheticProperty)?.shouldBeCached == true
- }
+ fun shouldCacheResource(resource: PropertyDescriptor) = (resource as? AndroidSyntheticProperty)?.shouldBeCached == true
}
private class SyntheticPartsGenerateContext(
@@ -63,7 +63,10 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
val classOrObject: KtClassOrObject,
val containerOptions: ContainerOptionsProxy)
- protected abstract fun isExperimental(clazz: KtClassOrObject): Boolean
+ protected abstract fun isExperimental(element: KtElement?): Boolean
+ protected abstract fun getGlobalCacheImpl(element: KtElement?): CacheImplementation
+
+ private fun ContainerOptionsProxy.getCacheOrDefault(element: KtElement?) = this.cache ?: getGlobalCacheImpl(element)
override fun applyProperty(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? {
val resultingDescriptor = resolvedCall.resultingDescriptor
@@ -79,7 +82,7 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
return if (targetCallable.name.asString() == AndroidConst.CLEAR_FUNCTION_NAME) {
val container = resolvedCall.getReceiverDeclarationDescriptor() as? ClassDescriptor ?: return null
- generateClearFindViewByIdCacheFunctionCall(receiver, container, c)
+ generateClearFindViewByIdCacheFunctionCall(receiver, resolvedCall, container, c)
}
else {
null
@@ -88,12 +91,13 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
private fun generateClearFindViewByIdCacheFunctionCall(
receiver: StackValue,
+ resolvedCall: ResolvedCall<*>,
container: ClassDescriptor,
c: ExpressionCodegenExtension.Context
): StackValue? {
val containerOptions = ContainerOptionsProxy.create(container)
- if (!containerOptions.cache.hasCache) {
+ if (!containerOptions.getCacheOrDefault(resolvedCall.call.calleeExpression).hasCache) {
return StackValue.functionCall(Type.VOID_TYPE) {}
}
@@ -119,7 +123,8 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
val container = resolvedCall.getReceiverDeclarationDescriptor() as? ClassDescriptor ?: return null
val containerOptions = ContainerOptionsProxy.create(container)
- return ResourcePropertyStackValue(receiver, c.typeMapper, resource, container, containerOptions, androidPackage)
+ return ResourcePropertyStackValue(receiver, c.typeMapper, resource, container,
+ containerOptions, androidPackage, getGlobalCacheImpl(resolvedCall.call.calleeExpression))
}
private fun ResolvedCall<*>.getReceiverDeclarationDescriptor(): ClassifierDescriptor? {
@@ -134,7 +139,7 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
if (container.kind != ClassKind.CLASS || container.isInner || DescriptorUtils.isLocal(container)) return
val containerOptions = ContainerOptionsProxy.create(container)
- if (containerOptions.cache == NO_CACHE) return
+ if (containerOptions.getCacheOrDefault(targetClass) == NO_CACHE) return
if (containerOptions.containerType == LAYOUT_CONTAINER && !isExperimental(targetClass)) {
return
@@ -184,7 +189,7 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
val iv = InstructionAdapter(methodVisitor)
val containerType = state.typeMapper.mapClass(container)
- val cacheImpl = CacheMechanism.get(containerOptions, iv, containerType)
+ val cacheImpl = CacheMechanism.get(containerOptions.getCacheOrDefault(classOrObject), iv, containerType)
cacheImpl.loadCache()
val lCacheIsNull = Label()
@@ -199,7 +204,7 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
}
private fun SyntheticPartsGenerateContext.generateCacheField() {
- val cacheImpl = CacheMechanism.getType(containerOptions)
+ val cacheImpl = CacheMechanism.getType(containerOptions.getCacheOrDefault(classOrObject))
classBuilder.newField(JvmDeclarationOrigin.NO_ORIGIN, ACC_PRIVATE, PROPERTY_NAME, cacheImpl.descriptor, null, null)
}
@@ -213,7 +218,7 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
methodVisitor.visitCode()
val iv = InstructionAdapter(methodVisitor)
- val cacheImpl = CacheMechanism.get(containerOptions, iv, containerAsmType)
+ val cacheImpl = CacheMechanism.get(containerOptions.getCacheOrDefault(classOrObject), iv, containerAsmType)
fun loadId() = iv.load(1, Type.INT_TYPE)
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 38ad611ba52..e9a2186afa7 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,7 @@
package org.jetbrains.kotlin.android.synthetic.codegen
import com.intellij.psi.PsiElement
+import kotlinx.android.extensions.CacheImplementation
import org.jetbrains.kotlin.android.synthetic.codegen.AbstractAndroidExtensionsExpressionCodegenExtension.Companion.ON_DESTROY_METHOD_NAME
import org.jetbrains.kotlin.android.synthetic.descriptors.ContainerOptionsProxy
import org.jetbrains.kotlin.codegen.ClassBuilder
@@ -30,9 +31,9 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.*
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.kotlin.android.synthetic.codegen.AbstractAndroidExtensionsExpressionCodegenExtension.Companion.CLEAR_CACHE_METHOD_NAME
+import org.jetbrains.kotlin.psi.KtElement
-class AndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptorExtension {
-
+abstract class AbstractAndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptorExtension {
override fun interceptClassBuilderFactory(
interceptedFactory: ClassBuilderFactory,
bindingContext: BindingContext,
@@ -41,6 +42,8 @@ class AndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptor
return AndroidOnDestroyClassBuilderFactory(interceptedFactory, bindingContext)
}
+ abstract fun getGlobalCacheImpl(element: KtElement): CacheImplementation
+
private inner class AndroidOnDestroyClassBuilderFactory(
private val delegateFactory: ClassBuilderFactory,
val bindingContext: BindingContext
@@ -108,6 +111,7 @@ class AndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptor
}
private fun generateClearCacheMethodCall() {
+ val currentClass = currentClass
if (name != ON_DESTROY_METHOD_NAME || currentClass == null) return
if (Type.getArgumentTypes(desc).isNotEmpty()) return
if (Type.getReturnType(desc) != Type.VOID_TYPE) return
@@ -116,7 +120,7 @@ class AndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptor
val container = bindingContext.get(BindingContext.CLASS, currentClass) ?: return
val entityOptions = ContainerOptionsProxy.create(container)
- if (!entityOptions.containerType.isFragment || !entityOptions.cache.hasCache) return
+ if (!entityOptions.containerType.isFragment || !(entityOptions.cache ?: getGlobalCacheImpl(currentClass)).hasCache) return
val iv = InstructionAdapter(this)
iv.load(0, containerType)
diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/CacheMechanisms.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/CacheMechanisms.kt
index 75a8fff6452..aff68de21a6 100644
--- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/CacheMechanisms.kt
+++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/CacheMechanisms.kt
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.android.synthetic.codegen
import kotlinx.android.extensions.CacheImplementation
-import org.jetbrains.kotlin.android.synthetic.descriptors.ContainerOptionsProxy
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.kotlin.android.synthetic.codegen.AbstractAndroidExtensionsExpressionCodegenExtension.Companion.PROPERTY_NAME
@@ -39,16 +38,16 @@ interface CacheMechanism {
fun putViewToCache(getView: () -> Unit)
companion object {
- fun getType(containerOptions: ContainerOptionsProxy): Type {
- return Type.getObjectType(when (containerOptions.cache) {
+ fun getType(cacheImpl: CacheImplementation): Type {
+ return Type.getObjectType(when (cacheImpl) {
CacheImplementation.SPARSE_ARRAY -> "android.util.SparseArray"
CacheImplementation.HASH_MAP -> HashMap::class.java.canonicalName
CacheImplementation.NO_CACHE -> throw IllegalArgumentException("Container should support cache")
}.replace('.', '/'))
}
- fun get(containerOptions: ContainerOptionsProxy, iv: InstructionAdapter, containerType: Type): CacheMechanism {
- return when (containerOptions.cache) {
+ fun get(cacheImpl: CacheImplementation, iv: InstructionAdapter, containerType: Type): CacheMechanism {
+ return when (cacheImpl) {
CacheImplementation.HASH_MAP -> HashMapCacheMechanism(iv, containerType)
CacheImplementation.SPARSE_ARRAY -> SparseArrayCacheMechanism(iv, containerType)
CacheImplementation.NO_CACHE -> throw IllegalArgumentException("Container should support cache")
diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/CliAndroidExtensionsExpressionCodegenExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/CliAndroidExtensionsExpressionCodegenExtension.kt
index 14bdfcd1103..8e76513b2f9 100644
--- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/CliAndroidExtensionsExpressionCodegenExtension.kt
+++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/CliAndroidExtensionsExpressionCodegenExtension.kt
@@ -16,8 +16,13 @@
package org.jetbrains.kotlin.android.synthetic.codegen
-import org.jetbrains.kotlin.psi.KtClassOrObject
+import kotlinx.android.extensions.CacheImplementation
+import org.jetbrains.kotlin.psi.KtElement
-class CliAndroidExtensionsExpressionCodegenExtension(val isExperimental: Boolean) : AbstractAndroidExtensionsExpressionCodegenExtension() {
- override fun isExperimental(clazz: KtClassOrObject) = isExperimental
+class CliAndroidExtensionsExpressionCodegenExtension(
+ val isExperimental: Boolean,
+ private val globalCacheImpl: CacheImplementation
+) : AbstractAndroidExtensionsExpressionCodegenExtension() {
+ override fun isExperimental(element: KtElement?) = isExperimental
+ override fun getGlobalCacheImpl(element: KtElement?) = globalCacheImpl
}
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/CliAndroidOnDestroyClassBuilderInterceptorExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/CliAndroidOnDestroyClassBuilderInterceptorExtension.kt
new file mode 100644
index 00000000000..e4fd4c341db
--- /dev/null
+++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/CliAndroidOnDestroyClassBuilderInterceptorExtension.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.codegen
+
+import kotlinx.android.extensions.CacheImplementation
+import org.jetbrains.kotlin.psi.KtElement
+
+class CliAndroidOnDestroyClassBuilderInterceptorExtension(
+ private val globalCacheImpl: CacheImplementation
+) : AbstractAndroidOnDestroyClassBuilderInterceptorExtension() {
+ override fun getGlobalCacheImpl(element: KtElement) = globalCacheImpl
+}
\ No newline at end of file
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 a17cadd86ed..e32af1cb08e 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
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.android.synthetic.codegen
+import kotlinx.android.extensions.CacheImplementation
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.codegen.AbstractAndroidExtensionsExpressionCodegenExtension.Companion.shouldCacheResource
import org.jetbrains.kotlin.android.synthetic.codegen.AbstractAndroidExtensionsExpressionCodegenExtension.Companion.CACHED_FIND_VIEW_BY_ID_METHOD_NAME
@@ -35,7 +36,8 @@ class ResourcePropertyStackValue(
val resource: PropertyDescriptor,
val container: ClassDescriptor,
private val containerOptions: ContainerOptionsProxy,
- private val androidPackage: String
+ private val androidPackage: String,
+ private val globalCacheImpl: CacheImplementation
) : StackValue(typeMapper.mapType(resource.returnType!!)) {
private val containerType get() = containerOptions.containerType
@@ -51,7 +53,7 @@ class ResourcePropertyStackValue(
val syntheticProperty = resource as AndroidSyntheticProperty
- if (containerOptions.cache.hasCache && shouldCacheResource(resource)) {
+ if ((containerOptions.cache ?: globalCacheImpl).hasCache && shouldCacheResource(resource)) {
val declarationDescriptorType = typeMapper.mapType(container)
receiver.put(declarationDescriptorType, v)
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
index bda1274c0b1..464dbdf29d2 100644
--- 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
@@ -28,13 +28,11 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
-class ContainerOptionsProxy(val containerType: AndroidContainerType, val cache: CacheImplementation) {
+class ContainerOptionsProxy(val containerType: 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 = SPARSE_ARRAY
-
fun create(container: ClassDescriptor): ContainerOptionsProxy {
if (container.kind != ClassKind.CLASS) {
return ContainerOptionsProxy(AndroidContainerType.UNKNOWN, NO_CACHE)
@@ -49,24 +47,24 @@ class ContainerOptionsProxy(val containerType: AndroidContainerType, val cache:
val supportsCache = container.source is KotlinSourceElement && containerType.doesSupportCache
return ContainerOptionsProxy(
containerType,
- if (supportsCache) DEFAULT_CACHE_IMPL else NO_CACHE)
+ if (supportsCache) null else NO_CACHE) // `null` here means "use global cache implementation setting"
}
- val cache = anno.getEnumValue(CACHE_NAME, DEFAULT_CACHE_IMPL) { valueOf(it) }
+ val cache = anno.getEnumValue(CACHE_NAME) { valueOf(it) }
return ContainerOptionsProxy(containerType, cache)
}
}
}
-private fun > AnnotationDescriptor.getEnumValue(name: String, defaultValue: E, factory: (String) -> E): E {
- val valueName = (allValueArguments[Name.identifier(name)] as? EnumValue)?.value?.name?.asString() ?: defaultValue.name
+private fun > AnnotationDescriptor.getEnumValue(name: String, factory: (String) -> E): E? {
+ val valueName = (allValueArguments[Name.identifier(name)] as? EnumValue)?.value?.name?.asString() ?: return null
return try {
factory(valueName)
}
catch (e: IllegalArgumentException) {
// Enum.valueOf() may throw this
- defaultValue
+ null
}
-}
+}
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-idea/android-extensions-idea.iml b/plugins/android-extensions/android-extensions-idea/android-extensions-idea.iml
index 824738f3711..d27956575ff 100644
--- a/plugins/android-extensions/android-extensions-idea/android-extensions-idea.iml
+++ b/plugins/android-extensions/android-extensions-idea/android-extensions-idea.iml
@@ -25,5 +25,6 @@
+
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/AndroidExtensionsProjectResolverExtension.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/AndroidExtensionsProjectResolverExtension.kt
index 4d8b7b2f923..4c68b3b3311 100644
--- a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/AndroidExtensionsProjectResolverExtension.kt
+++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/AndroidExtensionsProjectResolverExtension.kt
@@ -19,14 +19,12 @@ package org.jetbrains.kotlin.android.synthetic.idea
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.ProjectKeys
import com.intellij.openapi.externalSystem.model.project.ModuleData
-import com.intellij.openapi.externalSystem.model.project.ProjectData
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.util.Key
import org.gradle.api.Project
import org.gradle.tooling.model.idea.IdeaModule
-import org.gradle.tooling.model.idea.IdeaProject
-import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.ANDROID_COMPILER_PLUGIN_ID
+import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.DEFAULT_CACHE_IMPL_OPTION
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.EXPERIMENTAL_OPTION
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.ENABLED_OPTION
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
@@ -46,14 +44,22 @@ var DataNode.hasAndroidExtensionsPlugin: Boolean
var DataNode.isExperimental: Boolean
by NotNullableUserDataProperty(Key.create("ANDROID_EXTENSIONS_IS_EXPERIMENTAL"), false)
+private const val DEFAULT_CACHE_IMPLEMENTATION_DEFAULT_VALUE = "hashMap"
+
+var DataNode.defaultCacheImplementation: String
+ by NotNullableUserDataProperty(Key.create("ANDROID_EXTENSIONS_DEFAULT_CACHE_IMPL"),
+ DEFAULT_CACHE_IMPLEMENTATION_DEFAULT_VALUE)
+
interface AndroidExtensionsGradleModel : Serializable {
val hasAndroidExtensionsPlugin: Boolean
val isExperimental: Boolean
+ val defaultCacheImplementation: String
}
class AndroidExtensionsGradleModelImpl(
override val hasAndroidExtensionsPlugin: Boolean,
- override val isExperimental: Boolean
+ override val isExperimental: Boolean,
+ override val defaultCacheImplementation: String
) : AndroidExtensionsGradleModel
@Suppress("unused")
@@ -66,6 +72,7 @@ class AndroidExtensionsProjectResolverExtension : AbstractProjectResolverExtensi
ideModule.hasAndroidExtensionsPlugin = androidExtensionsModel.hasAndroidExtensionsPlugin
ideModule.isExperimental = androidExtensionsModel.isExperimental
+ ideModule.defaultCacheImplementation = androidExtensionsModel.defaultCacheImplementation
super.populateModuleExtraModels(gradleModule, ideModule)
}
@@ -82,7 +89,9 @@ class AndroidExtensionsModelBuilderService : ModelBuilderService {
override fun buildAll(modelName: String?, project: Project): Any {
val androidExtensionsPlugin = project.plugins.findPlugin("kotlin-android-extensions")
- val isExperimental = project.extensions.findByName("androidExtensions")?.let { ext ->
+ val androidExtensionsExtension = project.extensions.findByName("androidExtensions")
+
+ val isExperimental = androidExtensionsExtension?.let { ext ->
val isExperimentalMethod = ext::class.java.methods
.firstOrNull { it.name == "isExperimental" && it.parameterCount == 0 }
?: return@let false
@@ -90,7 +99,20 @@ class AndroidExtensionsModelBuilderService : ModelBuilderService {
isExperimentalMethod.invoke(ext) as? Boolean
} ?: false
- return AndroidExtensionsGradleModelImpl(androidExtensionsPlugin != null, isExperimental)
+ val defaultCacheImplementation = androidExtensionsExtension?.let { ext ->
+ val defaultCacheImplementationMethod = ext::class.java.methods.firstOrNull {
+ it.name == "getDefaultCacheImplementation" && it.parameterCount == 0
+ } ?: return@let DEFAULT_CACHE_IMPLEMENTATION_DEFAULT_VALUE
+
+ val enumValue = defaultCacheImplementationMethod.invoke(ext) ?: return@let DEFAULT_CACHE_IMPLEMENTATION_DEFAULT_VALUE
+
+ val optionNameMethod = enumValue::class.java.methods.firstOrNull { it.name == "getOptionName" && it.parameterCount == 0 }
+ ?: return@let DEFAULT_CACHE_IMPLEMENTATION_DEFAULT_VALUE
+
+ optionNameMethod.invoke(enumValue) as? String
+ } ?: DEFAULT_CACHE_IMPLEMENTATION_DEFAULT_VALUE
+
+ return AndroidExtensionsGradleModelImpl(androidExtensionsPlugin != null, isExperimental, defaultCacheImplementation)
}
}
@@ -113,6 +135,7 @@ class AndroidExtensionsGradleImportHandler : GradleProjectImportHandler {
if (moduleNode.hasAndroidExtensionsPlugin) {
newPluginOptions += makePluginOption(EXPERIMENTAL_OPTION.name, moduleNode.isExperimental.toString())
newPluginOptions += makePluginOption(ENABLED_OPTION.name, moduleNode.hasAndroidExtensionsPlugin.toString())
+ newPluginOptions += makePluginOption(DEFAULT_CACHE_IMPL_OPTION.name, moduleNode.defaultCacheImplementation)
}
commonArguments.pluginOptions = newPluginOptions.toTypedArray()
diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/ExperimentalUtils.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/ExperimentalUtils.kt
index 3377ea64f32..a43bc9cd91e 100644
--- a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/ExperimentalUtils.kt
+++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/ExperimentalUtils.kt
@@ -16,20 +16,25 @@
package org.jetbrains.kotlin.android.synthetic.idea
+import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.module.Module
+import kotlinx.android.extensions.CacheImplementation
+import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.ANDROID_COMPILER_PLUGIN_ID
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.EXPERIMENTAL_OPTION
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.ENABLED_OPTION
+import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.DEFAULT_CACHE_IMPL_OPTION
+import org.jetbrains.kotlin.android.synthetic.AndroidComponentRegistrar.Companion.parseCacheImplementationType
import org.jetbrains.kotlin.compiler.plugin.CliOption
import org.jetbrains.kotlin.idea.caches.resolve.ModuleSourceInfo
import org.jetbrains.kotlin.idea.facet.KotlinFacet
private val ANNOTATION_OPTION_PREFIX = "plugin:$ANDROID_COMPILER_PLUGIN_ID:"
-private fun Module.isOptionEnabledInFacet(option: CliOption): Boolean {
- val kotlinFacet = KotlinFacet.get(this) ?: return false
- val commonArgs = kotlinFacet.configuration.settings.compilerArguments ?: return false
+private fun Module.getOptionValueInFacet(option: CliOption): String? {
+ val kotlinFacet = KotlinFacet.get(this) ?: return null
+ val commonArgs = kotlinFacet.configuration.settings.compilerArguments ?: return null
val prefix = ANNOTATION_OPTION_PREFIX + option.name + "="
@@ -37,14 +42,25 @@ private fun Module.isOptionEnabledInFacet(option: CliOption): Boolean {
?.firstOrNull { it.startsWith(prefix) }
?.substring(prefix.length)
- return optionValue == "true"
+ return optionValue
+}
+
+private fun isTestMode(module: Module): Boolean {
+ return ApplicationManager.getApplication().isUnitTestMode && AndroidFacet.getInstance(module) != null
}
internal val Module.androidExtensionsIsEnabled: Boolean
- get() = isOptionEnabledInFacet(ENABLED_OPTION)
+ get() = isTestMode(this) || getOptionValueInFacet(ENABLED_OPTION) == "true"
internal val ModuleInfo.androidExtensionsIsExperimental: Boolean
get() {
val module = (this as? ModuleSourceInfo)?.module ?: return false
- return module.isOptionEnabledInFacet(EXPERIMENTAL_OPTION)
+ if (isTestMode(module)) return true
+ return module.getOptionValueInFacet(EXPERIMENTAL_OPTION) == "true"
+ }
+
+val ModuleInfo.androidExtensionsGlobalCacheImpl: CacheImplementation
+ get() {
+ val module = (this as? ModuleSourceInfo)?.module ?: return CacheImplementation.NO_CACHE
+ return parseCacheImplementationType(module.getOptionValueInFacet(DEFAULT_CACHE_IMPL_OPTION))
}
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/IDEAndroidExtensionsExpressionCodegenExtension.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/IDEAndroidExtensionsExpressionCodegenExtension.kt
index 0cd53e38a58..5dd3e57bf1a 100644
--- a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/IDEAndroidExtensionsExpressionCodegenExtension.kt
+++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/IDEAndroidExtensionsExpressionCodegenExtension.kt
@@ -16,13 +16,15 @@
package org.jetbrains.kotlin.android.synthetic.idea
+import kotlinx.android.extensions.CacheImplementation
import org.jetbrains.kotlin.android.synthetic.codegen.AbstractAndroidExtensionsExpressionCodegenExtension
import org.jetbrains.kotlin.idea.caches.resolve.getModuleInfo
-import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtElement
class IDEAndroidExtensionsExpressionCodegenExtension : AbstractAndroidExtensionsExpressionCodegenExtension() {
- override fun isExperimental(clazz: KtClassOrObject): Boolean {
- val moduleInfo = clazz.getModuleInfo() ?: return false
- return moduleInfo.androidExtensionsIsExperimental
- }
+ override fun isExperimental(element: KtElement?) =
+ element?.getModuleInfo()?.androidExtensionsIsExperimental ?: false
+
+ override fun getGlobalCacheImpl(element: KtElement?) =
+ element?.getModuleInfo()?.androidExtensionsGlobalCacheImpl ?: CacheImplementation.DEFAULT
}
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/IDEAndroidOnDestroyClassBuilderInterceptorExtension.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/IDEAndroidOnDestroyClassBuilderInterceptorExtension.kt
new file mode 100644
index 00000000000..b0399a8d3b7
--- /dev/null
+++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/IDEAndroidOnDestroyClassBuilderInterceptorExtension.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.idea
+
+import kotlinx.android.extensions.CacheImplementation
+import org.jetbrains.kotlin.android.synthetic.codegen.AbstractAndroidOnDestroyClassBuilderInterceptorExtension
+import org.jetbrains.kotlin.idea.caches.resolve.getModuleInfo
+import org.jetbrains.kotlin.psi.KtElement
+
+class IDEAndroidOnDestroyClassBuilderInterceptorExtension : AbstractAndroidOnDestroyClassBuilderInterceptorExtension() {
+ override fun getGlobalCacheImpl(element: KtElement) = element.getModuleInfo().androidExtensionsGlobalCacheImpl
+}
\ No newline at end of file
diff --git a/plugins/android-extensions/android-extensions-runtime/src/kotlinx/android/extensions/CacheImplementation.kt b/plugins/android-extensions/android-extensions-runtime/src/kotlinx/android/extensions/CacheImplementation.kt
index 57eafe10ff7..6ecb9b6f3b3 100644
--- a/plugins/android-extensions/android-extensions-runtime/src/kotlinx/android/extensions/CacheImplementation.kt
+++ b/plugins/android-extensions/android-extensions-runtime/src/kotlinx/android/extensions/CacheImplementation.kt
@@ -23,4 +23,8 @@ public enum class CacheImplementation {
public val hasCache: Boolean
get() = this != NO_CACHE
+
+ companion object {
+ val DEFAULT = HASH_MAP
+ }
}
\ No newline at end of file
diff --git a/plugins/plugins-tests/plugins-tests.iml b/plugins/plugins-tests/plugins-tests.iml
index 30adc2f1856..29cfa62c84b 100755
--- a/plugins/plugins-tests/plugins-tests.iml
+++ b/plugins/plugins-tests/plugins-tests.iml
@@ -42,5 +42,6 @@
+
\ No newline at end of file
diff --git a/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/synthetic/test/CompilerTestUtils.kt b/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/synthetic/test/CompilerTestUtils.kt
index 5515bbd98b8..a633dea5f0a 100755
--- a/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/synthetic/test/CompilerTestUtils.kt
+++ b/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/synthetic/test/CompilerTestUtils.kt
@@ -17,10 +17,11 @@
package org.jetbrains.kotlin.android.synthetic.test
import com.intellij.testFramework.registerServiceInstance
+import kotlinx.android.extensions.CacheImplementation
import org.jetbrains.kotlin.android.synthetic.AndroidConfigurationKeys
import org.jetbrains.kotlin.android.synthetic.AndroidExtensionPropertiesComponentContainerContributor
-import org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidExtensionsExpressionCodegenExtension
+import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidOnDestroyClassBuilderInterceptorExtension
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
import org.jetbrains.kotlin.android.synthetic.res.AndroidVariant
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidLayoutXmlFileManager
@@ -46,9 +47,9 @@ fun KtUsefulTestCase.createTestEnvironment(configuration: CompilerConfiguration,
val variants = listOf(AndroidVariant.createMainVariant(resDirectories))
project.registerServiceInstance(AndroidLayoutXmlFileManager::class.java, CliAndroidLayoutXmlFileManager(project, "test", variants))
- ExpressionCodegenExtension.registerExtension(project, CliAndroidExtensionsExpressionCodegenExtension(true))
+ ExpressionCodegenExtension.registerExtension(project, CliAndroidExtensionsExpressionCodegenExtension(true, CacheImplementation.DEFAULT))
StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor())
- ClassBuilderInterceptorExtension.registerExtension(project, AndroidOnDestroyClassBuilderInterceptorExtension())
+ ClassBuilderInterceptorExtension.registerExtension(project, CliAndroidOnDestroyClassBuilderInterceptorExtension(CacheImplementation.DEFAULT))
PackageFragmentProviderExtension.registerExtension(project, CliAndroidPackageFragmentProviderExtension(true))
addAndroidExtensionsRuntimeLibrary(myEnvironment)