Skip to content

Commit 5244c5e

Browse files
committed
Android Extensions: Add global cache flag in compiler plugin
1 parent 6ecf2e8 commit 5244c5e

File tree

16 files changed

+154
-58
lines changed

16 files changed

+154
-58
lines changed

idea/src/META-INF/android.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
<expressionCodegenExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.IDEAndroidExtensionsExpressionCodegenExtension"/>
7575
<defaultErrorMessages implementation="org.jetbrains.kotlin.android.synthetic.diagnostic.DefaultErrorMessagesAndroid"/>
7676
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.android.synthetic.AndroidExtensionPropertiesComponentContainerContributor"/>
77-
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension"/>
77+
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.IDEAndroidOnDestroyClassBuilderInterceptorExtension"/>
7878
<packageFragmentProviderExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.res.IDEAndroidPackageFragmentProviderExtension"/>
7979
<simpleNameReferenceExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidSimpleNameReferenceExtension"/>
8080
<kotlinIndicesHelperExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidIndicesHelperExtension"/>

plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ package org.jetbrains.kotlin.android.synthetic
1919
import com.intellij.mock.MockProject
2020
import com.intellij.openapi.extensions.Extensions
2121
import com.intellij.openapi.project.Project
22+
import kotlinx.android.extensions.CacheImplementation
2223
import org.jetbrains.kotlin.android.parcel.ParcelableCodegenExtension
2324
import org.jetbrains.kotlin.android.parcel.ParcelableDeclarationChecker
2425
import org.jetbrains.kotlin.android.parcel.ParcelableResolveExtension
25-
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension
2626
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidExtensionsExpressionCodegenExtension
27+
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidOnDestroyClassBuilderInterceptorExtension
2728
import org.jetbrains.kotlin.android.synthetic.diagnostic.AndroidExtensionPropertiesCallChecker
2829
import org.jetbrains.kotlin.android.synthetic.diagnostic.DefaultErrorMessagesAndroid
2930
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
@@ -49,32 +50,37 @@ import org.jetbrains.kotlin.android.synthetic.codegen.ParcelableClinitClassBuild
4950
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
5051

5152
object AndroidConfigurationKeys {
52-
val VARIANT: CompilerConfigurationKey<List<String>> = CompilerConfigurationKey.create<List<String>>("Android build variant")
53-
val PACKAGE: CompilerConfigurationKey<String> = CompilerConfigurationKey.create<String>("application package fq name")
54-
val EXPERIMENTAL: CompilerConfigurationKey<String> = CompilerConfigurationKey.create<String>("enable experimental features")
53+
val VARIANT = CompilerConfigurationKey.create<List<String>>("Android build variant")
54+
val PACKAGE = CompilerConfigurationKey.create<String>("application package fq name")
55+
val EXPERIMENTAL = CompilerConfigurationKey.create<String>("enable experimental features")
56+
val DEFAULT_CACHE_IMPL = CompilerConfigurationKey.create<String>("default cache implementation")
5557
}
5658

5759
class AndroidCommandLineProcessor : CommandLineProcessor {
5860
companion object {
5961
val ANDROID_COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.android"
6062

61-
val VARIANT_OPTION: CliOption = CliOption("variant", "<name;path>", "Android build variant", allowMultipleOccurrences = true)
62-
val PACKAGE_OPTION: CliOption = CliOption("package", "<fq name>", "Application package")
63-
val EXPERIMENTAL_OPTION: CliOption = CliOption("experimental", "true/false", "Enable experimental features", required = false)
63+
val VARIANT_OPTION = CliOption("variant", "<name;path>", "Android build variant", allowMultipleOccurrences = true)
64+
val PACKAGE_OPTION = CliOption("package", "<fq name>", "Application package")
65+
val EXPERIMENTAL_OPTION = CliOption("experimental", "true/false", "Enable experimental features", required = false)
66+
val DEFAULT_CACHE_IMPL_OPTION = CliOption(
67+
"defaultCacheImplementation", "hashMap/sparseArray/none", "Default cache implementation for module", required = false)
6468

6569
/* This option is just for saving Android Extensions status in Kotlin facet. It should not be supported from CLI. */
6670
val ENABLED_OPTION: CliOption = CliOption("enabled", "true/false", "Enable Android Extensions", required = false)
6771
}
6872

6973
override val pluginId: String = ANDROID_COMPILER_PLUGIN_ID
7074

71-
override val pluginOptions: Collection<CliOption> = listOf(VARIANT_OPTION, PACKAGE_OPTION, EXPERIMENTAL_OPTION)
75+
override val pluginOptions: Collection<CliOption>
76+
= listOf(VARIANT_OPTION, PACKAGE_OPTION, EXPERIMENTAL_OPTION, DEFAULT_CACHE_IMPL_OPTION)
7277

7378
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) {
7479
when (option) {
7580
VARIANT_OPTION -> configuration.appendList(AndroidConfigurationKeys.VARIANT, value)
7681
PACKAGE_OPTION -> configuration.put(AndroidConfigurationKeys.PACKAGE, value)
7782
EXPERIMENTAL_OPTION -> configuration.put(AndroidConfigurationKeys.EXPERIMENTAL, value)
83+
DEFAULT_CACHE_IMPL_OPTION -> configuration.put(AndroidConfigurationKeys.DEFAULT_CACHE_IMPL, value)
7884
else -> throw CliOptionProcessingException("Unknown option: ${option.name}")
7985
}
8086
}
@@ -87,12 +93,19 @@ class AndroidComponentRegistrar : ComponentRegistrar {
8793
SyntheticResolveExtension.registerExtension(project, ParcelableResolveExtension())
8894
ClassBuilderInterceptorExtension.registerExtension(project, ParcelableClinitClassBuilderInterceptorExtension())
8995
}
96+
97+
fun parseCacheImplementationType(s: String?): CacheImplementation = when (s) {
98+
"sparseArray" -> CacheImplementation.SPARSE_ARRAY
99+
"none" -> CacheImplementation.NO_CACHE
100+
else -> CacheImplementation.DEFAULT
101+
}
90102
}
91103

92104
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
93105
val applicationPackage = configuration.get(AndroidConfigurationKeys.PACKAGE)
94106
val variants = configuration.get(AndroidConfigurationKeys.VARIANT)?.mapNotNull { parseVariant(it) } ?: emptyList()
95107
val isExperimental = configuration.get(AndroidConfigurationKeys.EXPERIMENTAL) == "true"
108+
val globalCacheImpl = parseCacheImplementationType(configuration.get(AndroidConfigurationKeys.DEFAULT_CACHE_IMPL))
96109

97110
if (isExperimental) {
98111
registerParcelExtensions(project)
@@ -102,10 +115,10 @@ class AndroidComponentRegistrar : ComponentRegistrar {
102115
val layoutXmlFileManager = CliAndroidLayoutXmlFileManager(project, applicationPackage!!, variants)
103116
project.registerService(AndroidLayoutXmlFileManager::class.java, layoutXmlFileManager)
104117

105-
ExpressionCodegenExtension.registerExtension(project, CliAndroidExtensionsExpressionCodegenExtension(isExperimental))
118+
ExpressionCodegenExtension.registerExtension(project, CliAndroidExtensionsExpressionCodegenExtension(isExperimental, globalCacheImpl))
106119
StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor())
107120
Extensions.getRootArea().getExtensionPoint(DefaultErrorMessages.Extension.EP_NAME).registerExtension(DefaultErrorMessagesAndroid())
108-
ClassBuilderInterceptorExtension.registerExtension(project, AndroidOnDestroyClassBuilderInterceptorExtension())
121+
ClassBuilderInterceptorExtension.registerExtension(project, CliAndroidOnDestroyClassBuilderInterceptorExtension(globalCacheImpl))
109122
PackageFragmentProviderExtension.registerExtension(project, CliAndroidPackageFragmentProviderExtension(isExperimental))
110123
}
111124
}

plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AbstractAndroidExtensionsExpressionCodegenExtension.kt

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.jetbrains.kotlin.android.synthetic.codegen
1818

19+
import kotlinx.android.extensions.CacheImplementation
1920
import kotlinx.android.extensions.CacheImplementation.NO_CACHE
2021
import org.jetbrains.kotlin.android.synthetic.AndroidConst
2122
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidContainerType.LAYOUT_CONTAINER
@@ -32,6 +33,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState
3233
import org.jetbrains.kotlin.descriptors.*
3334
import org.jetbrains.kotlin.psi.KtClass
3435
import org.jetbrains.kotlin.psi.KtClassOrObject
36+
import org.jetbrains.kotlin.psi.KtElement
3537
import org.jetbrains.kotlin.resolve.DescriptorUtils
3638
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
3739
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
@@ -51,9 +53,7 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
5153
val CLEAR_CACHE_METHOD_NAME = "_\$_clearFindViewByIdCache"
5254
val ON_DESTROY_METHOD_NAME = "onDestroyView"
5355

54-
fun shouldCacheResource(resource: PropertyDescriptor): Boolean {
55-
return (resource as? AndroidSyntheticProperty)?.shouldBeCached == true
56-
}
56+
fun shouldCacheResource(resource: PropertyDescriptor) = (resource as? AndroidSyntheticProperty)?.shouldBeCached == true
5757
}
5858

5959
private class SyntheticPartsGenerateContext(
@@ -63,7 +63,10 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
6363
val classOrObject: KtClassOrObject,
6464
val containerOptions: ContainerOptionsProxy)
6565

66-
protected abstract fun isExperimental(clazz: KtClassOrObject): Boolean
66+
protected abstract fun isExperimental(element: KtElement?): Boolean
67+
protected abstract fun getGlobalCacheImpl(element: KtElement?): CacheImplementation
68+
69+
private fun ContainerOptionsProxy.getCacheOrDefault(element: KtElement?) = this.cache ?: getGlobalCacheImpl(element)
6770

6871
override fun applyProperty(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? {
6972
val resultingDescriptor = resolvedCall.resultingDescriptor
@@ -79,7 +82,7 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
7982

8083
return if (targetCallable.name.asString() == AndroidConst.CLEAR_FUNCTION_NAME) {
8184
val container = resolvedCall.getReceiverDeclarationDescriptor() as? ClassDescriptor ?: return null
82-
generateClearFindViewByIdCacheFunctionCall(receiver, container, c)
85+
generateClearFindViewByIdCacheFunctionCall(receiver, resolvedCall, container, c)
8386
}
8487
else {
8588
null
@@ -88,12 +91,13 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
8891

8992
private fun generateClearFindViewByIdCacheFunctionCall(
9093
receiver: StackValue,
94+
resolvedCall: ResolvedCall<*>,
9195
container: ClassDescriptor,
9296
c: ExpressionCodegenExtension.Context
9397
): StackValue? {
9498
val containerOptions = ContainerOptionsProxy.create(container)
9599

96-
if (!containerOptions.cache.hasCache) {
100+
if (!containerOptions.getCacheOrDefault(resolvedCall.call.calleeExpression).hasCache) {
97101
return StackValue.functionCall(Type.VOID_TYPE) {}
98102
}
99103

@@ -119,7 +123,8 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
119123
val container = resolvedCall.getReceiverDeclarationDescriptor() as? ClassDescriptor ?: return null
120124

121125
val containerOptions = ContainerOptionsProxy.create(container)
122-
return ResourcePropertyStackValue(receiver, c.typeMapper, resource, container, containerOptions, androidPackage)
126+
return ResourcePropertyStackValue(receiver, c.typeMapper, resource, container,
127+
containerOptions, androidPackage, getGlobalCacheImpl(resolvedCall.call.calleeExpression))
123128
}
124129

125130
private fun ResolvedCall<*>.getReceiverDeclarationDescriptor(): ClassifierDescriptor? {
@@ -134,7 +139,7 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
134139
if (container.kind != ClassKind.CLASS || container.isInner || DescriptorUtils.isLocal(container)) return
135140

136141
val containerOptions = ContainerOptionsProxy.create(container)
137-
if (containerOptions.cache == NO_CACHE) return
142+
if (containerOptions.getCacheOrDefault(targetClass) == NO_CACHE) return
138143

139144
if (containerOptions.containerType == LAYOUT_CONTAINER && !isExperimental(targetClass)) {
140145
return
@@ -184,7 +189,7 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
184189
val iv = InstructionAdapter(methodVisitor)
185190

186191
val containerType = state.typeMapper.mapClass(container)
187-
val cacheImpl = CacheMechanism.get(containerOptions, iv, containerType)
192+
val cacheImpl = CacheMechanism.get(containerOptions.getCacheOrDefault(classOrObject), iv, containerType)
188193

189194
cacheImpl.loadCache()
190195
val lCacheIsNull = Label()
@@ -199,7 +204,7 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
199204
}
200205

201206
private fun SyntheticPartsGenerateContext.generateCacheField() {
202-
val cacheImpl = CacheMechanism.getType(containerOptions)
207+
val cacheImpl = CacheMechanism.getType(containerOptions.getCacheOrDefault(classOrObject))
203208
classBuilder.newField(JvmDeclarationOrigin.NO_ORIGIN, ACC_PRIVATE, PROPERTY_NAME, cacheImpl.descriptor, null, null)
204209
}
205210

@@ -213,7 +218,7 @@ abstract class AbstractAndroidExtensionsExpressionCodegenExtension : ExpressionC
213218
methodVisitor.visitCode()
214219
val iv = InstructionAdapter(methodVisitor)
215220

216-
val cacheImpl = CacheMechanism.get(containerOptions, iv, containerAsmType)
221+
val cacheImpl = CacheMechanism.get(containerOptions.getCacheOrDefault(classOrObject), iv, containerAsmType)
217222

218223
fun loadId() = iv.load(1, Type.INT_TYPE)
219224

plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidOnDestroyClassBuilderInterceptorExtension.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.jetbrains.kotlin.android.synthetic.codegen
1818

1919
import com.intellij.psi.PsiElement
20+
import kotlinx.android.extensions.CacheImplementation
2021
import org.jetbrains.kotlin.android.synthetic.codegen.AbstractAndroidExtensionsExpressionCodegenExtension.Companion.ON_DESTROY_METHOD_NAME
2122
import org.jetbrains.kotlin.android.synthetic.descriptors.ContainerOptionsProxy
2223
import org.jetbrains.kotlin.codegen.ClassBuilder
@@ -30,9 +31,9 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
3031
import org.jetbrains.org.objectweb.asm.*
3132
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
3233
import org.jetbrains.kotlin.android.synthetic.codegen.AbstractAndroidExtensionsExpressionCodegenExtension.Companion.CLEAR_CACHE_METHOD_NAME
34+
import org.jetbrains.kotlin.psi.KtElement
3335

34-
class AndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptorExtension {
35-
36+
abstract class AbstractAndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptorExtension {
3637
override fun interceptClassBuilderFactory(
3738
interceptedFactory: ClassBuilderFactory,
3839
bindingContext: BindingContext,
@@ -41,6 +42,8 @@ class AndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptor
4142
return AndroidOnDestroyClassBuilderFactory(interceptedFactory, bindingContext)
4243
}
4344

45+
abstract fun getGlobalCacheImpl(element: KtElement): CacheImplementation
46+
4447
private inner class AndroidOnDestroyClassBuilderFactory(
4548
private val delegateFactory: ClassBuilderFactory,
4649
val bindingContext: BindingContext
@@ -108,6 +111,7 @@ class AndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptor
108111
}
109112

110113
private fun generateClearCacheMethodCall() {
114+
val currentClass = currentClass
111115
if (name != ON_DESTROY_METHOD_NAME || currentClass == null) return
112116
if (Type.getArgumentTypes(desc).isNotEmpty()) return
113117
if (Type.getReturnType(desc) != Type.VOID_TYPE) return
@@ -116,7 +120,7 @@ class AndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptor
116120

117121
val container = bindingContext.get(BindingContext.CLASS, currentClass) ?: return
118122
val entityOptions = ContainerOptionsProxy.create(container)
119-
if (!entityOptions.containerType.isFragment || !entityOptions.cache.hasCache) return
123+
if (!entityOptions.containerType.isFragment || !(entityOptions.cache ?: getGlobalCacheImpl(currentClass)).hasCache) return
120124

121125
val iv = InstructionAdapter(this)
122126
iv.load(0, containerType)

plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/CacheMechanisms.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package org.jetbrains.kotlin.android.synthetic.codegen
1818

1919
import kotlinx.android.extensions.CacheImplementation
20-
import org.jetbrains.kotlin.android.synthetic.descriptors.ContainerOptionsProxy
2120
import org.jetbrains.org.objectweb.asm.Type
2221
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
2322
import org.jetbrains.kotlin.android.synthetic.codegen.AbstractAndroidExtensionsExpressionCodegenExtension.Companion.PROPERTY_NAME
@@ -39,16 +38,16 @@ interface CacheMechanism {
3938
fun putViewToCache(getView: () -> Unit)
4039

4140
companion object {
42-
fun getType(containerOptions: ContainerOptionsProxy): Type {
43-
return Type.getObjectType(when (containerOptions.cache) {
41+
fun getType(cacheImpl: CacheImplementation): Type {
42+
return Type.getObjectType(when (cacheImpl) {
4443
CacheImplementation.SPARSE_ARRAY -> "android.util.SparseArray"
4544
CacheImplementation.HASH_MAP -> HashMap::class.java.canonicalName
4645
CacheImplementation.NO_CACHE -> throw IllegalArgumentException("Container should support cache")
4746
}.replace('.', '/'))
4847
}
4948

50-
fun get(containerOptions: ContainerOptionsProxy, iv: InstructionAdapter, containerType: Type): CacheMechanism {
51-
return when (containerOptions.cache) {
49+
fun get(cacheImpl: CacheImplementation, iv: InstructionAdapter, containerType: Type): CacheMechanism {
50+
return when (cacheImpl) {
5251
CacheImplementation.HASH_MAP -> HashMapCacheMechanism(iv, containerType)
5352
CacheImplementation.SPARSE_ARRAY -> SparseArrayCacheMechanism(iv, containerType)
5453
CacheImplementation.NO_CACHE -> throw IllegalArgumentException("Container should support cache")

plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/CliAndroidExtensionsExpressionCodegenExtension.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@
1616

1717
package org.jetbrains.kotlin.android.synthetic.codegen
1818

19-
import org.jetbrains.kotlin.psi.KtClassOrObject
19+
import kotlinx.android.extensions.CacheImplementation
20+
import org.jetbrains.kotlin.psi.KtElement
2021

21-
class CliAndroidExtensionsExpressionCodegenExtension(val isExperimental: Boolean) : AbstractAndroidExtensionsExpressionCodegenExtension() {
22-
override fun isExperimental(clazz: KtClassOrObject) = isExperimental
22+
class CliAndroidExtensionsExpressionCodegenExtension(
23+
val isExperimental: Boolean,
24+
private val globalCacheImpl: CacheImplementation
25+
) : AbstractAndroidExtensionsExpressionCodegenExtension() {
26+
override fun isExperimental(element: KtElement?) = isExperimental
27+
override fun getGlobalCacheImpl(element: KtElement?) = globalCacheImpl
2328
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2010-2017 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jetbrains.kotlin.android.synthetic.codegen
18+
19+
import kotlinx.android.extensions.CacheImplementation
20+
import org.jetbrains.kotlin.psi.KtElement
21+
22+
class CliAndroidOnDestroyClassBuilderInterceptorExtension(
23+
private val globalCacheImpl: CacheImplementation
24+
) : AbstractAndroidOnDestroyClassBuilderInterceptorExtension() {
25+
override fun getGlobalCacheImpl(element: KtElement) = globalCacheImpl
26+
}

0 commit comments

Comments
 (0)