Refactoring: rename android-compiler-plugin to android-extensions-compiler
This commit is contained in:
+1
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.android.synthetic.AndroidComponentRegistrar
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.android.synthetic.diagnostic.AndroidExtensionPropertiesCallChecker
|
||||
import org.jetbrains.kotlin.android.synthetic.diagnostic.DefaultErrorMessagesAndroid
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidVariant
|
||||
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidLayoutXmlFileManager
|
||||
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidPackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.CliOption
|
||||
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
|
||||
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||
import org.jetbrains.kotlin.container.StorageComponentContainer
|
||||
import org.jetbrains.kotlin.container.useInstance
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
|
||||
object AndroidConfigurationKeys {
|
||||
val VARIANT: CompilerConfigurationKey<List<String>> = CompilerConfigurationKey.create<List<String>>("Android build variant")
|
||||
val PACKAGE: CompilerConfigurationKey<String> = CompilerConfigurationKey.create<String>("application package fq name")
|
||||
}
|
||||
|
||||
class AndroidCommandLineProcessor : CommandLineProcessor {
|
||||
companion object {
|
||||
val ANDROID_COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.android"
|
||||
|
||||
val VARIANT_OPTION: CliOption = CliOption("variant", "<name;path>", "Android build variant", allowMultipleOccurrences = true)
|
||||
val PACKAGE_OPTION: CliOption = CliOption("package", "<fq name>", "Application package")
|
||||
}
|
||||
|
||||
override val pluginId: String = ANDROID_COMPILER_PLUGIN_ID
|
||||
|
||||
override val pluginOptions: Collection<CliOption> = listOf(VARIANT_OPTION, PACKAGE_OPTION)
|
||||
|
||||
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) {
|
||||
when (option) {
|
||||
VARIANT_OPTION -> {
|
||||
val paths = configuration.getList(AndroidConfigurationKeys.VARIANT).toMutableList()
|
||||
paths.add(value)
|
||||
configuration.put(AndroidConfigurationKeys.VARIANT, paths)
|
||||
}
|
||||
PACKAGE_OPTION -> configuration.put(AndroidConfigurationKeys.PACKAGE, value)
|
||||
else -> throw CliOptionProcessingException("Unknown option: ${option.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidComponentRegistrar : ComponentRegistrar {
|
||||
|
||||
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
||||
val applicationPackage = configuration.get(AndroidConfigurationKeys.PACKAGE)
|
||||
val variants = configuration.get(AndroidConfigurationKeys.VARIANT)?.mapNotNull { parseVariant(it) } ?: emptyList()
|
||||
|
||||
if (variants.isNotEmpty() && !applicationPackage.isNullOrBlank()) {
|
||||
val layoutXmlFileManager = CliAndroidLayoutXmlFileManager(project, applicationPackage!!, variants)
|
||||
project.registerService(AndroidLayoutXmlFileManager::class.java, layoutXmlFileManager)
|
||||
|
||||
ExpressionCodegenExtension.registerExtension(project, AndroidExpressionCodegenExtension())
|
||||
StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor())
|
||||
Extensions.getRootArea().getExtensionPoint(DefaultErrorMessages.Extension.EP_NAME).registerExtension(DefaultErrorMessagesAndroid())
|
||||
ClassBuilderInterceptorExtension.registerExtension(project, AndroidOnDestroyClassBuilderInterceptorExtension())
|
||||
PackageFragmentProviderExtension.registerExtension(project, CliAndroidPackageFragmentProviderExtension())
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseVariant(s: String): AndroidVariant? {
|
||||
val parts = s.split(';')
|
||||
if (parts.size < 2) return null
|
||||
return AndroidVariant(parts[0], parts.drop(1))
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidExtensionPropertiesComponentContainerContributor : StorageComponentContainerContributor {
|
||||
override fun addDeclarations(container: StorageComponentContainer, platform: TargetPlatform) {
|
||||
if (platform is JvmPlatform) {
|
||||
container.useInstance(AndroidExtensionPropertiesCallChecker())
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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
|
||||
|
||||
|
||||
object AndroidConst {
|
||||
val SYNTHETIC_PACKAGE: String = "kotlinx.android.synthetic"
|
||||
val SYNTHETIC_PACKAGE_PATH_LENGTH = SYNTHETIC_PACKAGE.count { it == '.' } + 1
|
||||
|
||||
val SYNTHETIC_SUBPACKAGES: List<String> = SYNTHETIC_PACKAGE.split('.').fold(arrayListOf<String>()) { list, segment ->
|
||||
val prevSegment = list.lastOrNull()?.let { "$it." } ?: ""
|
||||
list += "$prevSegment$segment"
|
||||
list
|
||||
}
|
||||
|
||||
val ANDROID_NAMESPACE: String = "android"
|
||||
val ID_ATTRIBUTE_NO_NAMESPACE: String = "id"
|
||||
val ID_ATTRIBUTE: String = "$ANDROID_NAMESPACE:$ID_ATTRIBUTE_NO_NAMESPACE"
|
||||
val CLASS_ATTRIBUTE_NO_NAMESPACE: String = "class"
|
||||
|
||||
val ID_DECLARATION_PREFIX = "@+id/"
|
||||
val ID_USAGE_PREFIX = "@id/"
|
||||
val XML_ID_PREFIXES = arrayOf(ID_DECLARATION_PREFIX, ID_USAGE_PREFIX)
|
||||
|
||||
val CLEAR_FUNCTION_NAME = "clearFindViewByIdCache"
|
||||
|
||||
|
||||
//TODO FqName / ClassId
|
||||
|
||||
val VIEW_FQNAME = "android.view.View"
|
||||
val VIEWSTUB_FQNAME = "android.view.ViewStub"
|
||||
|
||||
val ACTIVITY_FQNAME = "android.app.Activity"
|
||||
val FRAGMENT_FQNAME = "android.app.Fragment"
|
||||
val SUPPORT_V4_PACKAGE = "android.support.v4"
|
||||
val SUPPORT_FRAGMENT_FQNAME = "$SUPPORT_V4_PACKAGE.app.Fragment"
|
||||
val SUPPORT_FRAGMENT_ACTIVITY_FQNAME = "$SUPPORT_V4_PACKAGE.app.FragmentActivity"
|
||||
|
||||
val IGNORED_XML_WIDGET_TYPES = setOf("requestFocus", "merge", "tag", "check", "blink")
|
||||
|
||||
val FQNAME_RESOLVE_PACKAGES = listOf("android.widget", "android.webkit", "android.view")
|
||||
}
|
||||
|
||||
fun androidIdToName(id: String): String? {
|
||||
for (prefix in AndroidConst.XML_ID_PREFIXES) {
|
||||
if (id.startsWith(prefix)) {
|
||||
return id.substring(prefix.length)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun isWidgetTypeIgnored(xmlType: String): Boolean {
|
||||
return (xmlType.isEmpty() || xmlType in AndroidConst.IGNORED_XML_WIDGET_TYPES)
|
||||
}
|
||||
|
||||
internal fun <T> List<T>.forEachUntilLast(operation: (T) -> Unit) {
|
||||
val lastIndex = lastIndex
|
||||
forEachIndexed { i, t ->
|
||||
if (i < lastIndex) {
|
||||
operation(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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
|
||||
|
||||
import org.xml.sax.Attributes
|
||||
import org.xml.sax.helpers.DefaultHandler
|
||||
import java.util.HashMap
|
||||
|
||||
class AndroidXmlHandler(private val elementCallback: (String, String) -> Unit) : DefaultHandler() {
|
||||
|
||||
override fun startDocument() {
|
||||
super.startDocument()
|
||||
}
|
||||
|
||||
override fun endDocument() {
|
||||
super.endDocument()
|
||||
}
|
||||
|
||||
override fun startElement(uri: String, localName: String, qName: String, attributes: Attributes) {
|
||||
if (isWidgetTypeIgnored(localName)) return
|
||||
val attributesMap = attributes.toMap()
|
||||
val idAttribute = attributesMap[AndroidConst.ID_ATTRIBUTE_NO_NAMESPACE]
|
||||
val widgetType = attributesMap[AndroidConst.CLASS_ATTRIBUTE_NO_NAMESPACE] ?: localName
|
||||
val name = idAttribute?.let { androidIdToName(idAttribute) }
|
||||
if (name != null) elementCallback(name, widgetType)
|
||||
}
|
||||
|
||||
override fun endElement(uri: String?, localName: String, qName: String) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun Attributes.toMap(): HashMap<String, String> {
|
||||
val res = HashMap<String, String>()
|
||||
for (index in 0..length - 1) {
|
||||
val attrName = getLocalName(index)!!
|
||||
val attrVal = getValue(index)!!
|
||||
res[attrName] = attrVal
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
+391
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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 org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||
import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticFunction
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticProperty
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.FunctionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
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.lazy.descriptors.LazyClassDescriptor
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||
import org.jetbrains.kotlin.types.lowerIfFlexible
|
||||
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
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
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),
|
||||
SUPPORT_FRAGMENT_ACTIVITY(AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME, supportsCache = true),
|
||||
SUPPORT_FRAGMENT(AndroidConst.SUPPORT_FRAGMENT_FQNAME, supportsCache = true, fragment = true),
|
||||
VIEW(AndroidConst.VIEW_FQNAME),
|
||||
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.SUPPORT_FRAGMENT_ACTIVITY_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY
|
||||
AndroidConst.SUPPORT_FRAGMENT_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT
|
||||
AndroidConst.VIEW_FQNAME -> AndroidClassType.VIEW
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (descriptor is LazyJavaClassDescriptor) {
|
||||
val androidClassType = getClassTypeInternal(DescriptorUtils.getFqName(descriptor).asString())
|
||||
if (androidClassType != null) return androidClassType
|
||||
}
|
||||
else if (descriptor is LazyClassDescriptor) { // For tests (FakeActivity)
|
||||
val androidClassType = getClassTypeInternal(DescriptorUtils.getFqName(descriptor).toString())
|
||||
if (androidClassType != null) return androidClassType
|
||||
}
|
||||
|
||||
for (supertype in descriptor.typeConstructor.supertypes) {
|
||||
val declarationDescriptor = supertype.constructor.declarationDescriptor
|
||||
if (declarationDescriptor != null) {
|
||||
val androidClassType = getClassType(declarationDescriptor)
|
||||
if (androidClassType != AndroidClassType.UNKNOWN) return androidClassType
|
||||
}
|
||||
}
|
||||
|
||||
return AndroidClassType.UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||
companion object {
|
||||
private val PROPERTY_NAME = "_\$_findViewCache"
|
||||
private val CACHED_FIND_VIEW_BY_ID_METHOD_NAME = "_\$_findCachedViewById"
|
||||
val CLEAR_CACHE_METHOD_NAME = "_\$_clearFindViewByIdCache"
|
||||
val ON_DESTROY_METHOD_NAME = "onDestroyView"
|
||||
|
||||
fun isCacheSupported(receiverDescriptor: ClassDescriptor, descriptor: PropertyDescriptor? = null): Boolean {
|
||||
val receiverIsKotlinClass = receiverDescriptor.source is KotlinSourceElement
|
||||
return receiverIsKotlinClass && when (descriptor) {
|
||||
is AndroidSyntheticProperty -> !descriptor.alwaysCastToView
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SyntheticPartsGenerateContext(
|
||||
val classBuilder: ClassBuilder,
|
||||
val state: GenerationState,
|
||||
val descriptor: ClassDescriptor,
|
||||
val classOrObject: KtClassOrObject,
|
||||
val androidClassType: AndroidClassType)
|
||||
|
||||
override fun applyProperty(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? {
|
||||
val resultingDescriptor = resolvedCall.resultingDescriptor
|
||||
return if (resultingDescriptor is PropertyDescriptor) {
|
||||
return generateSyntheticPropertyCall(receiver, resolvedCall, c, resultingDescriptor)
|
||||
}
|
||||
else null
|
||||
}
|
||||
|
||||
override fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? {
|
||||
val resultingDescriptor = resolvedCall.resultingDescriptor
|
||||
return if (resultingDescriptor is FunctionDescriptor) {
|
||||
return generateSyntheticFunctionCall(receiver, resolvedCall, c, resultingDescriptor)
|
||||
}
|
||||
else null
|
||||
}
|
||||
|
||||
private fun generateSyntheticFunctionCall(
|
||||
receiver: StackValue,
|
||||
resolvedCall: ResolvedCall<*>,
|
||||
c: ExpressionCodegenExtension.Context,
|
||||
functionDescriptor: FunctionDescriptor
|
||||
): StackValue? {
|
||||
if (functionDescriptor !is AndroidSyntheticFunction) return null
|
||||
if (functionDescriptor.name.asString() != AndroidConst.CLEAR_FUNCTION_NAME) return null
|
||||
|
||||
val receiverDescriptor = resolvedCall.getReceiverDeclarationDescriptor() as? ClassDescriptor ?: return null
|
||||
if (!isCacheSupported(receiverDescriptor)) return StackValue.functionCall(Type.VOID_TYPE) {}
|
||||
|
||||
val androidClassType = AndroidClassType.getClassType(receiverDescriptor)
|
||||
if (androidClassType == AndroidClassType.UNKNOWN) return null
|
||||
|
||||
return StackValue.functionCall(Type.VOID_TYPE) {
|
||||
val bytecodeClassName = c.typeMapper.mapType(receiverDescriptor).internalName
|
||||
|
||||
receiver.put(c.typeMapper.mapType(receiverDescriptor), it)
|
||||
it.invokevirtual(bytecodeClassName, CLEAR_CACHE_METHOD_NAME, "()V", false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateSyntheticPropertyCall(
|
||||
receiver: StackValue,
|
||||
resolvedCall: ResolvedCall<*>,
|
||||
c: ExpressionCodegenExtension.Context,
|
||||
descriptor: PropertyDescriptor
|
||||
): StackValue? {
|
||||
if (descriptor !is AndroidSyntheticProperty) return null
|
||||
val packageFragment = descriptor.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)
|
||||
|
||||
return SyntheticProperty(receiver, c.typeMapper, descriptor, receiverDescriptor, androidClassType, androidPackage)
|
||||
}
|
||||
|
||||
private class SyntheticProperty(
|
||||
val receiver: StackValue,
|
||||
val typeMapper: KotlinTypeMapper,
|
||||
val propertyDescriptor: PropertyDescriptor,
|
||||
val receiverDescriptor: ClassDescriptor,
|
||||
val androidClassType: AndroidClassType,
|
||||
val androidPackage: String
|
||||
) : StackValue(typeMapper.mapType(propertyDescriptor.returnType!!)) {
|
||||
|
||||
override fun putSelector(type: Type, v: InstructionAdapter) {
|
||||
val returnTypeString = typeMapper.mapType(propertyDescriptor.type.lowerIfFlexible()).className
|
||||
if (AndroidConst.FRAGMENT_FQNAME == returnTypeString || AndroidConst.SUPPORT_FRAGMENT_FQNAME == returnTypeString) {
|
||||
return putSelectorForFragment(v)
|
||||
}
|
||||
|
||||
if (androidClassType.supportsCache && isCacheSupported(receiverDescriptor, propertyDescriptor)) {
|
||||
val declarationDescriptorType = typeMapper.mapType(receiverDescriptor)
|
||||
receiver.put(declarationDescriptorType, v)
|
||||
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.name.asString(), "I")
|
||||
v.invokevirtual(declarationDescriptorType.internalName, CACHED_FIND_VIEW_BY_ID_METHOD_NAME, "(I)Landroid/view/View;", false)
|
||||
}
|
||||
else {
|
||||
when (androidClassType) {
|
||||
AndroidClassType.ACTIVITY, AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY, AndroidClassType.VIEW -> {
|
||||
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 -> {
|
||||
receiver.put(Type.getType("L${androidClassType.internalClassName};"), v)
|
||||
v.invokevirtual(androidClassType.internalClassName, "getView", "()Landroid/view/View;", false)
|
||||
getResourceId(v)
|
||||
v.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false)
|
||||
}
|
||||
else -> throw IllegalStateException("Invalid Android class type: $androidClassType") // Should never occur
|
||||
}
|
||||
}
|
||||
|
||||
v.checkcast(this.type)
|
||||
}
|
||||
|
||||
private fun putSelectorForFragment(v: InstructionAdapter) {
|
||||
receiver.put(Type.getType("L${androidClassType.internalClassName};"), v)
|
||||
|
||||
when (androidClassType) {
|
||||
AndroidClassType.ACTIVITY, AndroidClassType.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 -> {
|
||||
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 -> {
|
||||
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)
|
||||
}
|
||||
else -> throw IllegalStateException("Invalid Android class type: $androidClassType") // Should never occur
|
||||
}
|
||||
|
||||
v.checkcast(this.type)
|
||||
}
|
||||
|
||||
fun getResourceId(v: InstructionAdapter) {
|
||||
v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.name.asString(), "I")
|
||||
}
|
||||
}
|
||||
|
||||
private fun ResolvedCall<*>.getReceiverDeclarationDescriptor(): ClassifierDescriptor? {
|
||||
return (extensionReceiver as ReceiverValue).type.constructor.declarationDescriptor
|
||||
}
|
||||
|
||||
override fun generateClassSyntheticParts(
|
||||
classBuilder: ClassBuilder,
|
||||
state: GenerationState,
|
||||
classOrObject: KtClassOrObject,
|
||||
descriptor: ClassDescriptor
|
||||
) {
|
||||
if (descriptor.kind != ClassKind.CLASS || descriptor.isInner || DescriptorUtils.isLocal(descriptor)) return
|
||||
|
||||
// Do not generate anything if class is not supported
|
||||
val androidClassType = AndroidClassType.getClassType(descriptor)
|
||||
if (androidClassType == AndroidClassType.UNKNOWN) return
|
||||
|
||||
val context = SyntheticPartsGenerateContext(classBuilder, state, descriptor, classOrObject, androidClassType)
|
||||
context.generateCachedFindViewByIdFunction()
|
||||
context.generateClearCacheFunction()
|
||||
|
||||
if (androidClassType.fragment) {
|
||||
val classMembers = descriptor.unsubstitutedMemberScope.getContributedDescriptors()
|
||||
val onDestroy = classMembers.firstOrNull { it is FunctionDescriptor && it.isOnDestroyFunction() }
|
||||
if (onDestroy == null) {
|
||||
context.generateOnDestroyFunctionForFragment()
|
||||
}
|
||||
}
|
||||
|
||||
classBuilder.newField(JvmDeclarationOrigin.NO_ORIGIN, ACC_PRIVATE, PROPERTY_NAME, "Ljava/util/HashMap;", null, null)
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.isOnDestroyFunction(): Boolean {
|
||||
return kind == CallableMemberDescriptor.Kind.DECLARATION
|
||||
&& name.asString() == ON_DESTROY_METHOD_NAME
|
||||
&& (visibility == Visibilities.INHERITED || visibility == Visibilities.PUBLIC)
|
||||
&& (valueParameters.isEmpty())
|
||||
&& (typeParameters.isEmpty())
|
||||
}
|
||||
|
||||
// This generates a simple onDestroy(): Unit = super.onDestroy() function.
|
||||
// CLEAR_CACHE_METHOD_NAME() method call will be inserted in ClassBuilder interceptor.
|
||||
private fun SyntheticPartsGenerateContext.generateOnDestroyFunctionForFragment() {
|
||||
val methodVisitor = classBuilder.newMethod(JvmDeclarationOrigin.NO_ORIGIN, ACC_PUBLIC, ON_DESTROY_METHOD_NAME, "()V", null, null)
|
||||
methodVisitor.visitCode()
|
||||
val iv = InstructionAdapter(methodVisitor)
|
||||
|
||||
val classType = state.typeMapper.mapClass(descriptor)
|
||||
|
||||
iv.load(0, classType)
|
||||
iv.invokespecial(state.typeMapper.mapClass(descriptor.getSuperClassOrAny()).internalName, ON_DESTROY_METHOD_NAME, "()V", false)
|
||||
iv.areturn(Type.VOID_TYPE)
|
||||
|
||||
FunctionCodegen.endVisit(methodVisitor, ON_DESTROY_METHOD_NAME, classOrObject)
|
||||
}
|
||||
|
||||
private fun SyntheticPartsGenerateContext.generateClearCacheFunction() {
|
||||
val methodVisitor = classBuilder.newMethod(JvmDeclarationOrigin.NO_ORIGIN, ACC_PUBLIC, CLEAR_CACHE_METHOD_NAME, "()V", null, null)
|
||||
methodVisitor.visitCode()
|
||||
val iv = InstructionAdapter(methodVisitor)
|
||||
|
||||
val classType = state.typeMapper.mapClass(descriptor)
|
||||
val className = classType.internalName
|
||||
|
||||
fun loadCache() {
|
||||
iv.load(0, classType)
|
||||
iv.getfield(className, PROPERTY_NAME, "Ljava/util/HashMap;")
|
||||
}
|
||||
|
||||
loadCache()
|
||||
val lCacheIsNull = Label()
|
||||
iv.ifnull(lCacheIsNull)
|
||||
|
||||
loadCache()
|
||||
iv.invokevirtual("java/util/HashMap", "clear", "()V", false)
|
||||
|
||||
iv.visitLabel(lCacheIsNull)
|
||||
iv.areturn(Type.VOID_TYPE)
|
||||
FunctionCodegen.endVisit(methodVisitor, CLEAR_CACHE_METHOD_NAME, classOrObject)
|
||||
}
|
||||
|
||||
private fun SyntheticPartsGenerateContext.generateCachedFindViewByIdFunction() {
|
||||
val classType = state.typeMapper.mapClass(descriptor)
|
||||
val className = classType.internalName
|
||||
|
||||
val viewType = Type.getObjectType("android/view/View")
|
||||
|
||||
val methodVisitor = classBuilder.newMethod(
|
||||
JvmDeclarationOrigin.NO_ORIGIN, ACC_PUBLIC, CACHED_FIND_VIEW_BY_ID_METHOD_NAME, "(I)Landroid/view/View;", null, null)
|
||||
methodVisitor.visitCode()
|
||||
val iv = InstructionAdapter(methodVisitor)
|
||||
|
||||
fun loadCache() {
|
||||
iv.load(0, classType)
|
||||
iv.getfield(className, PROPERTY_NAME, "Ljava/util/HashMap;")
|
||||
}
|
||||
|
||||
fun loadId() = iv.load(1, Type.INT_TYPE)
|
||||
|
||||
// Get cache property
|
||||
loadCache()
|
||||
|
||||
val lCacheNonNull = Label()
|
||||
iv.ifnonnull(lCacheNonNull)
|
||||
|
||||
// Init cache if null
|
||||
iv.load(0, classType)
|
||||
iv.anew(Type.getType("Ljava/util/HashMap;"))
|
||||
iv.dup()
|
||||
iv.invokespecial("java/util/HashMap", "<init>", "()V", false)
|
||||
iv.putfield(className, PROPERTY_NAME, "Ljava/util/HashMap;")
|
||||
|
||||
// Get View from cache
|
||||
iv.visitLabel(lCacheNonNull)
|
||||
loadCache()
|
||||
loadId()
|
||||
iv.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false)
|
||||
iv.invokevirtual("java/util/HashMap", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", false)
|
||||
iv.checkcast(viewType)
|
||||
iv.store(2, viewType)
|
||||
|
||||
val lViewNonNull = Label()
|
||||
iv.load(2, viewType)
|
||||
iv.ifnonnull(lViewNonNull)
|
||||
|
||||
// Resolve View via findViewById if not in cache
|
||||
iv.load(0, classType)
|
||||
when (androidClassType) {
|
||||
AndroidClassType.ACTIVITY, AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY, AndroidClassType.VIEW -> {
|
||||
loadId()
|
||||
iv.invokevirtual(className, "findViewById", "(I)Landroid/view/View;", false)
|
||||
}
|
||||
AndroidClassType.FRAGMENT, AndroidClassType.SUPPORT_FRAGMENT -> {
|
||||
iv.invokevirtual(className, "getView", "()Landroid/view/View;", false)
|
||||
loadId()
|
||||
iv.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false)
|
||||
}
|
||||
else -> throw IllegalStateException("Can't generate code for $androidClassType")
|
||||
}
|
||||
iv.store(2, viewType)
|
||||
|
||||
// Store resolved View in cache
|
||||
loadCache()
|
||||
loadId()
|
||||
iv.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false)
|
||||
iv.load(2, viewType)
|
||||
iv.invokevirtual("java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false)
|
||||
iv.pop()
|
||||
|
||||
iv.visitLabel(lViewNonNull)
|
||||
iv.load(2, viewType)
|
||||
iv.areturn(viewType)
|
||||
|
||||
FunctionCodegen.endVisit(methodVisitor, CACHED_FIND_VIEW_BY_ID_METHOD_NAME, classOrObject)
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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 com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactory
|
||||
import org.jetbrains.kotlin.codegen.DelegatingClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class AndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptorExtension {
|
||||
|
||||
override fun interceptClassBuilderFactory(
|
||||
interceptedFactory: ClassBuilderFactory,
|
||||
bindingContext: BindingContext,
|
||||
diagnostics: DiagnosticSink
|
||||
): ClassBuilderFactory {
|
||||
return AndroidOnDestroyClassBuilderFactory(interceptedFactory, bindingContext)
|
||||
}
|
||||
|
||||
private inner class AndroidOnDestroyClassBuilderFactory(
|
||||
private val delegateFactory: ClassBuilderFactory,
|
||||
val bindingContext: BindingContext
|
||||
) : ClassBuilderFactory {
|
||||
|
||||
override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder {
|
||||
return AndroidOnDestroyCollectorClassBuilder(delegateFactory.newClassBuilder(origin), bindingContext)
|
||||
}
|
||||
|
||||
override fun getClassBuilderMode() = delegateFactory.classBuilderMode
|
||||
|
||||
override fun asText(builder: ClassBuilder?): String? {
|
||||
return delegateFactory.asText((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder)
|
||||
}
|
||||
|
||||
override fun asBytes(builder: ClassBuilder?): ByteArray? {
|
||||
return delegateFactory.asBytes((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
delegateFactory.close()
|
||||
}
|
||||
}
|
||||
|
||||
private inner class AndroidOnDestroyCollectorClassBuilder(
|
||||
internal val delegateClassBuilder: ClassBuilder,
|
||||
val bindingContext: BindingContext
|
||||
) : DelegatingClassBuilder() {
|
||||
private var currentClass: KtClass? = null
|
||||
private var currentClassName: String? = null
|
||||
|
||||
override fun getDelegate() = delegateClassBuilder
|
||||
|
||||
override fun defineClass(
|
||||
origin: PsiElement?,
|
||||
version: Int,
|
||||
access: Int,
|
||||
name: String,
|
||||
signature: String?,
|
||||
superName: String,
|
||||
interfaces: Array<out String>
|
||||
) {
|
||||
if (origin is KtClass) {
|
||||
currentClass = origin
|
||||
currentClassName = name
|
||||
}
|
||||
super.defineClass(origin, version, access, name, signature, superName, interfaces)
|
||||
}
|
||||
|
||||
override fun newMethod(
|
||||
origin: JvmDeclarationOrigin,
|
||||
access: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor {
|
||||
return object : MethodVisitor(Opcodes.ASM5, super.newMethod(origin, access, name, desc, signature, exceptions)) {
|
||||
override fun visitInsn(opcode: Int) {
|
||||
if (opcode == Opcodes.RETURN) {
|
||||
generateClearCacheMethodCall()
|
||||
}
|
||||
|
||||
super.visitInsn(opcode)
|
||||
}
|
||||
|
||||
private fun generateClearCacheMethodCall() {
|
||||
if (name != AndroidExpressionCodegenExtension.ON_DESTROY_METHOD_NAME || currentClass == null) return
|
||||
if (Type.getArgumentTypes(desc).size != 0) return
|
||||
if (Type.getReturnType(desc) != Type.VOID_TYPE) return
|
||||
|
||||
val classType = currentClassName?.let { Type.getObjectType(it) } ?: return
|
||||
|
||||
val descriptor = bindingContext.get(BindingContext.CLASS, currentClass) as? ClassDescriptor ?: return
|
||||
val androidClassType = AndroidClassType.getClassType(descriptor)
|
||||
if (!androidClassType.fragment) return
|
||||
|
||||
val iv = InstructionAdapter(this)
|
||||
iv.load(0, classType)
|
||||
iv.invokevirtual(currentClassName, AndroidExpressionCodegenExtension.CLEAR_CACHE_METHOD_NAME, "()V", false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.android.synthetic.res.*
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
import java.util.*
|
||||
|
||||
class AndroidSyntheticPackageData(
|
||||
val moduleData: AndroidModuleData,
|
||||
val forView: Boolean,
|
||||
val isDeprecated: Boolean,
|
||||
val resources: List<AndroidResource>)
|
||||
|
||||
class AndroidSyntheticPackageFragmentDescriptor(
|
||||
module: ModuleDescriptor,
|
||||
fqName: FqName,
|
||||
val packageData: AndroidSyntheticPackageData,
|
||||
private val lazyContext: LazySyntheticElementResolveContext,
|
||||
private val storageManager: StorageManager
|
||||
) : PackageFragmentDescriptorImpl(module, fqName) {
|
||||
private val scope = AndroidExtensionPropertiesScope()
|
||||
override fun getMemberScope(): MemberScope = scope
|
||||
|
||||
private inner class AndroidExtensionPropertiesScope : MemberScopeImpl() {
|
||||
private val properties = storageManager.createLazyValue {
|
||||
val packageFragmentDescriptor = this@AndroidSyntheticPackageFragmentDescriptor
|
||||
|
||||
val context = lazyContext()
|
||||
val widgetReceivers = context.getWidgetReceivers(packageData.forView)
|
||||
val fragmentTypes = context.fragmentTypes
|
||||
|
||||
val properties = ArrayList<PropertyDescriptor>(0)
|
||||
for (resource in packageData.resources) {
|
||||
when (resource) {
|
||||
is AndroidResource.Widget -> {
|
||||
val resolvedWidget = resource.resolve(module)
|
||||
for (receiver in widgetReceivers) {
|
||||
properties += genPropertyForWidget(packageFragmentDescriptor, receiver, resolvedWidget, context)
|
||||
}
|
||||
}
|
||||
is AndroidResource.Fragment -> if (!packageData.forView) {
|
||||
for ((receiverType, type) in fragmentTypes) {
|
||||
properties += genPropertyForFragment(packageFragmentDescriptor, receiverType, type, resource, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
properties
|
||||
}
|
||||
|
||||
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean) =
|
||||
properties().filter { kindFilter.acceptsKinds(DescriptorKindFilter.VARIABLES_MASK) && nameFilter(it.name) }
|
||||
|
||||
override fun getContributedVariables(name: Name, location: LookupLocation) = properties().filter { it.name == name }
|
||||
|
||||
override fun printScopeStructure(p: Printer) {
|
||||
p.println(javaClass.simpleName)
|
||||
}
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.android.synthetic.AndroidConst
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import java.util.*
|
||||
|
||||
class LazySyntheticElementResolveContext(private val module: ModuleDescriptor, private val storageManager: StorageManager) {
|
||||
private val context = storageManager.createLazyValue {
|
||||
module.createResolveContext()
|
||||
}
|
||||
|
||||
internal operator fun invoke() = context()
|
||||
|
||||
private fun ModuleDescriptor.createResolveContext(): SyntheticElementResolveContext {
|
||||
fun find(fqName: String) = module.findClassAcrossModuleDependencies(ClassId.topLevel(FqName(fqName)))
|
||||
|
||||
val viewDescriptor = find(AndroidConst.VIEW_FQNAME) ?: return SyntheticElementResolveContext.ERROR_CONTEXT
|
||||
val activityDescriptor = find(AndroidConst.ACTIVITY_FQNAME) ?: return SyntheticElementResolveContext.ERROR_CONTEXT
|
||||
val fragmentDescriptor = find(AndroidConst.FRAGMENT_FQNAME)
|
||||
val supportActivityDescriptor = find(AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME)
|
||||
val supportFragmentDescriptor = find(AndroidConst.SUPPORT_FRAGMENT_FQNAME)
|
||||
|
||||
return SyntheticElementResolveContext(
|
||||
viewDescriptor.defaultType,
|
||||
activityDescriptor.defaultType,
|
||||
fragmentDescriptor?.defaultType,
|
||||
supportActivityDescriptor?.defaultType,
|
||||
supportFragmentDescriptor?.defaultType)
|
||||
}
|
||||
}
|
||||
|
||||
internal class SyntheticElementResolveContext(
|
||||
val viewType: KotlinType,
|
||||
val activityType: KotlinType,
|
||||
val fragmentType: KotlinType?,
|
||||
val supportActivityType: KotlinType?,
|
||||
val supportFragmentType: KotlinType?) {
|
||||
companion object {
|
||||
private fun errorType() = ErrorUtils.createErrorType("")
|
||||
val ERROR_CONTEXT = SyntheticElementResolveContext(errorType(), errorType(), null, null, null)
|
||||
}
|
||||
|
||||
private val widgetReceivers by lazy {
|
||||
val receivers = ArrayList<KotlinType>(3)
|
||||
receivers += activityType
|
||||
fragmentType?.let { receivers += it }
|
||||
supportFragmentType?.let { receivers += it }
|
||||
receivers
|
||||
}
|
||||
|
||||
val fragmentTypes: List<Pair<KotlinType, KotlinType>> by lazy {
|
||||
if (fragmentType == null) {
|
||||
emptyList<Pair<KotlinType, KotlinType>>()
|
||||
}
|
||||
else {
|
||||
val types = ArrayList<Pair<KotlinType, KotlinType>>(4)
|
||||
types += Pair(activityType, fragmentType)
|
||||
types += Pair(fragmentType, fragmentType)
|
||||
if (supportActivityType != null && supportFragmentType != null) {
|
||||
types += Pair(supportFragmentType, supportFragmentType)
|
||||
types += Pair(supportActivityType, supportFragmentType)
|
||||
}
|
||||
types
|
||||
}
|
||||
}
|
||||
|
||||
fun getWidgetReceivers(forView: Boolean): List<KotlinType> {
|
||||
if (forView) return listOf(viewType)
|
||||
return widgetReceivers
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
|
||||
class PredefinedPackageFragmentDescriptor(
|
||||
fqName: String,
|
||||
module: ModuleDescriptor,
|
||||
private val storageManager: StorageManager,
|
||||
val subpackages: List<PackageFragmentDescriptor> = emptyList(),
|
||||
private val descriptors: (PredefinedPackageFragmentDescriptor) -> Collection<DeclarationDescriptor> = { emptyList() }
|
||||
) : PackageFragmentDescriptorImpl(module, FqName(fqName)) {
|
||||
private val calculatedDescriptors = storageManager.createLazyValue {
|
||||
descriptors(this)
|
||||
}
|
||||
|
||||
private val scope = PredefinedScope()
|
||||
override fun getMemberScope() = scope
|
||||
|
||||
inner class PredefinedScope : MemberScopeImpl() {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun getContributedVariables(name: Name, location: LookupLocation) =
|
||||
calculatedDescriptors().filter { it is PropertyDescriptor && it.name == name } as List<PropertyDescriptor>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun getContributedFunctions(name: Name, location: LookupLocation) =
|
||||
calculatedDescriptors().filter { it is FunctionDescriptor && it.name == name } as List<FunctionDescriptor>
|
||||
|
||||
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean) =
|
||||
calculatedDescriptors().filter { nameFilter(it.name) && kindFilter.accepts(it) }
|
||||
|
||||
override fun printScopeStructure(p: Printer) {
|
||||
p.println(javaClass.simpleName)
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.diagnostic
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.android.synthetic.diagnostic.ErrorsAndroid.*
|
||||
import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticProperty
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
|
||||
class AndroidExtensionPropertiesCallChecker : CallChecker {
|
||||
override fun <F : CallableDescriptor> check(resolvedCall: ResolvedCall<F>, context: BasicCallResolutionContext) {
|
||||
val expression = context.call.calleeExpression ?: return
|
||||
|
||||
val propertyDescriptor = resolvedCall.resultingDescriptor as? PropertyDescriptor ?: return
|
||||
val containingPackage = propertyDescriptor.containingDeclaration as? AndroidSyntheticPackageFragmentDescriptor ?: return
|
||||
val androidSyntheticProperty = propertyDescriptor as? AndroidSyntheticProperty ?: return
|
||||
|
||||
with (context.trace) {
|
||||
checkUnresolvedWidgetType(expression, androidSyntheticProperty)
|
||||
checkDeprecated(expression, containingPackage)
|
||||
}
|
||||
}
|
||||
|
||||
private fun DiagnosticSink.checkDeprecated(expression: KtExpression, packageDescriptor: AndroidSyntheticPackageFragmentDescriptor) {
|
||||
if (packageDescriptor.packageData.isDeprecated) {
|
||||
report(SYNTHETIC_DEPRECATED_PACKAGE.on(expression))
|
||||
}
|
||||
}
|
||||
|
||||
private fun DiagnosticSink.checkUnresolvedWidgetType(expression: KtExpression, property: AndroidSyntheticProperty) {
|
||||
if (!property.isErrorType) return
|
||||
val type = property.errorType ?: return
|
||||
|
||||
val warning = if (type.contains('.')) SYNTHETIC_UNRESOLVED_WIDGET_TYPE else SYNTHETIC_INVALID_WIDGET_TYPE
|
||||
report(warning.on(expression, type))
|
||||
}
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.diagnostic
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
|
||||
|
||||
class DefaultErrorMessagesAndroid : DefaultErrorMessages.Extension {
|
||||
|
||||
private companion object {
|
||||
val MAP = DiagnosticFactoryToRendererMap("Android")
|
||||
|
||||
init {
|
||||
MAP.put(ErrorsAndroid.SYNTHETIC_INVALID_WIDGET_TYPE,
|
||||
"Widget has an invalid type ''{0}''. Please specify the fully qualified widget class name in XML",
|
||||
Renderers.TO_STRING)
|
||||
|
||||
MAP.put(ErrorsAndroid.SYNTHETIC_UNRESOLVED_WIDGET_TYPE,
|
||||
"Widget has an unresolved type ''{0}'', and thus it was upcasted to ''android.view.View''",
|
||||
Renderers.TO_STRING)
|
||||
|
||||
MAP.put(ErrorsAndroid.SYNTHETIC_DEPRECATED_PACKAGE,
|
||||
"Use properties from the build variant packages")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMap() = MAP
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.diagnostic;
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
|
||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
|
||||
import static org.jetbrains.kotlin.diagnostics.Severity.WARNING;
|
||||
|
||||
public interface ErrorsAndroid {
|
||||
DiagnosticFactory1<KtExpression, String> SYNTHETIC_INVALID_WIDGET_TYPE = DiagnosticFactory1.create(WARNING);
|
||||
DiagnosticFactory1<KtExpression, String> SYNTHETIC_UNRESOLVED_WIDGET_TYPE = DiagnosticFactory1.create(WARNING);
|
||||
DiagnosticFactory0<KtExpression> SYNTHETIC_DEPRECATED_PACKAGE = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
Object _initializer = new Object() {
|
||||
{
|
||||
Errors.Initializer.initializeFactoryNames(ErrorsAndroid.class);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.res
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleServiceManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import java.util.*
|
||||
|
||||
class AndroidVariantData(val variant: AndroidVariant, private val layouts: Map<String, List<PsiFile>>): Map<String, List<PsiFile>> by layouts
|
||||
class AndroidModuleData(val module: AndroidModule, private val variants: List<AndroidVariantData>): Iterable<AndroidVariantData> by variants {
|
||||
companion object {
|
||||
val EMPTY = AndroidModuleData(AndroidModule("android", listOf()), listOf())
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AndroidLayoutXmlFileManager(val project: Project) {
|
||||
|
||||
abstract val androidModule: AndroidModule?
|
||||
|
||||
open fun propertyToXmlAttributes(propertyDescriptor: PropertyDescriptor): List<PsiElement> = listOf()
|
||||
|
||||
open fun getModuleData(): AndroidModuleData {
|
||||
val androidModule = androidModule ?: return AndroidModuleData.EMPTY
|
||||
return AndroidModuleData(androidModule, androidModule.variants.map { getVariantData(it) })
|
||||
}
|
||||
|
||||
fun getVariantData(variant: AndroidVariant): AndroidVariantData {
|
||||
val psiManager = PsiManager.getInstance(project)
|
||||
val fileManager = VirtualFileManager.getInstance()
|
||||
|
||||
fun VirtualFile.getAllChildren(): List<VirtualFile> {
|
||||
val allChildren = arrayListOf<VirtualFile>()
|
||||
val currentChildren = children ?: emptyArray()
|
||||
for (child in currentChildren) {
|
||||
if (child.isDirectory) {
|
||||
allChildren.addAll(child.getAllChildren())
|
||||
}
|
||||
else {
|
||||
allChildren.add(child)
|
||||
}
|
||||
}
|
||||
return allChildren
|
||||
}
|
||||
|
||||
val resDirectories = variant.resDirectories.map { fileManager.findFileByUrl("file://$it") }
|
||||
val allChildren = resDirectories.flatMap { it?.getAllChildren() ?: listOf() }
|
||||
|
||||
val allLayoutFiles = allChildren.filter { it.parent.name.startsWith("layout") && it.name.toLowerCase().endsWith(".xml") }
|
||||
val allLayoutPsiFiles = allLayoutFiles.fold(ArrayList<PsiFile>(allLayoutFiles.size)) { list, file ->
|
||||
val psiFile = psiManager.findFile(file)
|
||||
if (psiFile != null && psiFile.parent != null) {
|
||||
list += psiFile
|
||||
}
|
||||
list
|
||||
}
|
||||
|
||||
val layoutNameToXmlFiles = allLayoutPsiFiles
|
||||
.groupBy { it.name.substringBeforeLast('.') }
|
||||
.mapValues { it.value.sortedBy { it.parent!!.name.length } }
|
||||
|
||||
return AndroidVariantData(variant, layoutNameToXmlFiles)
|
||||
}
|
||||
|
||||
fun extractResources(files: List<PsiFile>, module: ModuleDescriptor): List<AndroidResource> {
|
||||
return filterDuplicates(doExtractResources(files, module))
|
||||
}
|
||||
|
||||
protected abstract fun doExtractResources(files: List<PsiFile>, module: ModuleDescriptor): List<AndroidResource>
|
||||
|
||||
protected fun parseAndroidResource(id: String, tag: String, sourceElement: PsiElement?): AndroidResource {
|
||||
return when (tag) {
|
||||
"fragment" -> AndroidResource.Fragment(id, sourceElement)
|
||||
"include" -> AndroidResource.Widget(id, AndroidConst.VIEW_FQNAME, sourceElement)
|
||||
else -> AndroidResource.Widget(id, tag, sourceElement)
|
||||
}
|
||||
}
|
||||
|
||||
private fun filterDuplicates(resources: List<AndroidResource>): List<AndroidResource> {
|
||||
val resourceMap = linkedMapOf<String, AndroidResource>()
|
||||
val resourcesToExclude = hashSetOf<String>()
|
||||
|
||||
for (res in resources) {
|
||||
if (resourceMap.contains(res.id)) {
|
||||
val existing = resourceMap[res.id]!!
|
||||
|
||||
if (!res.sameClass(existing)) {
|
||||
resourcesToExclude.add(res.id)
|
||||
}
|
||||
else if (res is AndroidResource.Widget && existing is AndroidResource.Widget) {
|
||||
// Widgets with the same id but different types exist.
|
||||
if (res.xmlType != existing.xmlType && existing.xmlType != AndroidConst.VIEW_FQNAME) {
|
||||
resourceMap.put(res.id, AndroidResource.Widget(res.id, AndroidConst.VIEW_FQNAME, res.sourceElement))
|
||||
}
|
||||
}
|
||||
}
|
||||
else resourceMap.put(res.id, res)
|
||||
}
|
||||
resourcesToExclude.forEach { resourceMap.remove(it) }
|
||||
return resourceMap.values.toList()
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
fun getInstance(module: Module): AndroidLayoutXmlFileManager? {
|
||||
val service = ModuleServiceManager.getService(module, AndroidLayoutXmlFileManager::class.java)
|
||||
return service ?: module.getComponent(AndroidLayoutXmlFileManager::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.res
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||
import org.jetbrains.kotlin.android.synthetic.descriptors.*
|
||||
import org.jetbrains.kotlin.android.synthetic.forEachUntilLast
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
|
||||
abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProviderExtension {
|
||||
protected abstract fun getLayoutXmlFileManager(project: Project, moduleInfo: ModuleInfo?): AndroidLayoutXmlFileManager?
|
||||
|
||||
override fun getPackageFragmentProvider(
|
||||
project: Project,
|
||||
module: ModuleDescriptor,
|
||||
storageManager: StorageManager,
|
||||
trace: BindingTrace,
|
||||
moduleInfo: ModuleInfo?
|
||||
): PackageFragmentProvider? {
|
||||
val layoutXmlFileManager = getLayoutXmlFileManager(project, moduleInfo) ?: return null
|
||||
|
||||
val moduleData = layoutXmlFileManager.getModuleData()
|
||||
|
||||
val lazyContext = LazySyntheticElementResolveContext(module, storageManager)
|
||||
|
||||
val allPackageDescriptors = arrayListOf<PackageFragmentDescriptor>()
|
||||
val packagesToLookupInCompletion = arrayListOf<PackageFragmentDescriptor>()
|
||||
|
||||
// Packages with synthetic properties
|
||||
for (variantData in moduleData) {
|
||||
for ((layoutName, layouts) in variantData) {
|
||||
fun createPackageFragment(fqName: String, forView: Boolean, isDeprecated: Boolean = false) {
|
||||
val resources = layoutXmlFileManager.extractResources(layouts, module)
|
||||
val packageData = AndroidSyntheticPackageData(moduleData, forView, isDeprecated, resources)
|
||||
val packageDescriptor = AndroidSyntheticPackageFragmentDescriptor(
|
||||
module, FqName(fqName), packageData, lazyContext, storageManager)
|
||||
packagesToLookupInCompletion += packageDescriptor
|
||||
allPackageDescriptors += packageDescriptor
|
||||
}
|
||||
|
||||
val packageFqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name + '.' + layoutName
|
||||
|
||||
createPackageFragment(packageFqName, false)
|
||||
createPackageFragment(packageFqName + ".view", true)
|
||||
}
|
||||
}
|
||||
|
||||
// Empty middle packages
|
||||
AndroidConst.SYNTHETIC_SUBPACKAGES.forEachUntilLast { s ->
|
||||
allPackageDescriptors += PredefinedPackageFragmentDescriptor(s, module, storageManager)
|
||||
}
|
||||
|
||||
for (variantData in moduleData) {
|
||||
val fqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name
|
||||
allPackageDescriptors += PredefinedPackageFragmentDescriptor(fqName, module, storageManager)
|
||||
}
|
||||
|
||||
// Package with clearFindViewByIdCache()
|
||||
AndroidConst.SYNTHETIC_SUBPACKAGES.last().let { s ->
|
||||
val packageDescriptor = PredefinedPackageFragmentDescriptor(s, module, storageManager, packagesToLookupInCompletion) { descriptor ->
|
||||
lazyContext().getWidgetReceivers(false).map { genClearCacheFunction(descriptor, it) }
|
||||
}
|
||||
packagesToLookupInCompletion += packageDescriptor
|
||||
allPackageDescriptors += packageDescriptor
|
||||
}
|
||||
|
||||
return AndroidSyntheticPackageFragmentProvider(allPackageDescriptors)
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidSyntheticPackageFragmentProvider(
|
||||
val packageFragments: Collection<PackageFragmentDescriptor>
|
||||
) : PackageFragmentProvider {
|
||||
override fun getPackageFragments(fqName: FqName) = packageFragments.filter { it.fqName == fqName }
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean) =
|
||||
packageFragments.asSequence()
|
||||
.map { it.fqName }
|
||||
.filter { !it.isRoot && it.parent() == fqName }
|
||||
.toList()
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.res
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.android.synthetic.AndroidXmlHandler
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import java.io.ByteArrayInputStream
|
||||
import javax.xml.parsers.SAXParser
|
||||
import javax.xml.parsers.SAXParserFactory
|
||||
|
||||
class CliAndroidLayoutXmlFileManager(
|
||||
project: Project,
|
||||
private val applicationPackage: String,
|
||||
private val variants: List<AndroidVariant>
|
||||
) : AndroidLayoutXmlFileManager(project) {
|
||||
private companion object {
|
||||
val LOG = Logger.getInstance(CliAndroidLayoutXmlFileManager::class.java)
|
||||
}
|
||||
|
||||
override val androidModule = AndroidModule(applicationPackage, variants)
|
||||
|
||||
private val saxParser: SAXParser = initSAX()
|
||||
|
||||
override fun doExtractResources(files: List<PsiFile>, module: ModuleDescriptor): List<AndroidResource> {
|
||||
val resources = arrayListOf<AndroidResource>()
|
||||
|
||||
val handler = AndroidXmlHandler { id, tag ->
|
||||
resources += parseAndroidResource(id, tag, null)
|
||||
}
|
||||
|
||||
for (file in files) {
|
||||
try {
|
||||
val inputStream = ByteArrayInputStream(file.virtualFile.contentsToByteArray())
|
||||
saxParser.parse(inputStream, handler)
|
||||
} catch (e: Throwable) {
|
||||
LOG.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
return resources
|
||||
}
|
||||
|
||||
protected fun initSAX(): SAXParser {
|
||||
val saxFactory = SAXParserFactory.newInstance()
|
||||
saxFactory.isNamespaceAware = true
|
||||
return saxFactory.newSAXParser()
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.res
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
|
||||
class CliAndroidPackageFragmentProviderExtension : AndroidPackageFragmentProviderExtension() {
|
||||
override fun getLayoutXmlFileManager(project: Project, moduleInfo: ModuleInfo?): AndroidLayoutXmlFileManager? {
|
||||
return ServiceManager.getService(project, AndroidLayoutXmlFileManager::class.java)
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.res
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.CachedValue
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
|
||||
|
||||
class AndroidVariant(val name: String, val resDirectories: List<String>) {
|
||||
val packageName: String = name
|
||||
val isMainVariant: Boolean
|
||||
get() = name == "main"
|
||||
|
||||
companion object {
|
||||
fun createMainVariant(resDirectories: List<String>) = AndroidVariant("main", resDirectories)
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidModule(val applicationPackage: String, val variants: List<AndroidVariant>) {
|
||||
override fun equals(other: Any?) = other is AndroidModule && applicationPackage == other.applicationPackage
|
||||
override fun hashCode() = applicationPackage.hashCode()
|
||||
}
|
||||
|
||||
sealed class AndroidResource(val id: String, val sourceElement: PsiElement?) {
|
||||
open fun sameClass(other: AndroidResource): Boolean = false
|
||||
|
||||
class Widget(
|
||||
id: String,
|
||||
val xmlType: String,
|
||||
sourceElement: PsiElement?
|
||||
) : AndroidResource(id, sourceElement) {
|
||||
override fun sameClass(other: AndroidResource) = other is Widget
|
||||
}
|
||||
|
||||
class Fragment(id: String, sourceElement: PsiElement?) : AndroidResource(id, sourceElement) {
|
||||
override fun sameClass(other: AndroidResource) = other is Fragment
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> cachedValue(project: Project, result: () -> CachedValueProvider.Result<T>): CachedValue<T> {
|
||||
return CachedValuesManager.getManager(project).createCachedValue(result, false)
|
||||
}
|
||||
|
||||
class ResolvedWidget(val widget: AndroidResource.Widget, val viewClassDescriptor: ClassDescriptor?) {
|
||||
val isErrorType: Boolean
|
||||
get() = viewClassDescriptor == null
|
||||
|
||||
val errorType: String?
|
||||
get() = if (isErrorType) widget.xmlType else null
|
||||
}
|
||||
|
||||
fun AndroidResource.Widget.resolve(module: ModuleDescriptor): ResolvedWidget {
|
||||
fun resolve(fqName: String) = module.findClassAcrossModuleDependencies(ClassId.topLevel(FqName(fqName)))
|
||||
|
||||
if ('.' in xmlType) {
|
||||
return ResolvedWidget(this, resolve(xmlType))
|
||||
}
|
||||
|
||||
for (packageName in AndroidConst.FQNAME_RESOLVE_PACKAGES) {
|
||||
val classDescriptor = resolve("$packageName.$xmlType")
|
||||
if (classDescriptor != null) {
|
||||
return ResolvedWidget(this, classDescriptor)
|
||||
}
|
||||
}
|
||||
|
||||
return ResolvedWidget(this, null)
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.res
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||
import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.android.synthetic.descriptors.SyntheticElementResolveContext
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
|
||||
private class XmlSourceElement(override val psi: PsiElement) : PsiSourceElement
|
||||
|
||||
internal fun genClearCacheFunction(packageFragmentDescriptor: PackageFragmentDescriptor, receiverType: KotlinType): FunctionDescriptor {
|
||||
val function = object : AndroidSyntheticFunction, SimpleFunctionDescriptorImpl(
|
||||
packageFragmentDescriptor,
|
||||
null,
|
||||
Annotations.EMPTY,
|
||||
Name.identifier(AndroidConst.CLEAR_FUNCTION_NAME),
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
SourceElement.NO_SOURCE) {}
|
||||
|
||||
val unitType = packageFragmentDescriptor.builtIns.unitType
|
||||
function.initialize(receiverType, null, emptyList(), emptyList(), unitType, Modality.FINAL, Visibilities.PUBLIC)
|
||||
return function
|
||||
}
|
||||
|
||||
internal fun genPropertyForWidget(
|
||||
packageFragmentDescriptor: AndroidSyntheticPackageFragmentDescriptor,
|
||||
receiverType: KotlinType,
|
||||
resolvedWidget: ResolvedWidget,
|
||||
context: SyntheticElementResolveContext
|
||||
): PropertyDescriptor {
|
||||
val sourceEl = resolvedWidget.widget.sourceElement?.let { XmlSourceElement(it) } ?: SourceElement.NO_SOURCE
|
||||
|
||||
val classDescriptor = resolvedWidget.viewClassDescriptor
|
||||
val type = classDescriptor?.let {
|
||||
val defaultType = classDescriptor.defaultType
|
||||
if (defaultType.constructor.parameters.isEmpty())
|
||||
defaultType
|
||||
else
|
||||
KotlinTypeImpl.create(Annotations.EMPTY, classDescriptor, false,
|
||||
defaultType.constructor.parameters.map { StarProjectionImpl(it) })
|
||||
} ?: context.viewType
|
||||
|
||||
return genProperty(resolvedWidget.widget.id, receiverType, type, packageFragmentDescriptor, sourceEl, context, resolvedWidget.errorType)
|
||||
}
|
||||
|
||||
internal fun genPropertyForFragment(
|
||||
packageFragmentDescriptor: PackageFragmentDescriptor,
|
||||
receiverType: KotlinType,
|
||||
type: KotlinType,
|
||||
fragment: AndroidResource.Fragment,
|
||||
context: SyntheticElementResolveContext
|
||||
): PropertyDescriptor {
|
||||
val sourceElement = fragment.sourceElement?.let { XmlSourceElement(it) } ?: SourceElement.NO_SOURCE
|
||||
return genProperty(fragment.id, receiverType, type, packageFragmentDescriptor, sourceElement, context, null)
|
||||
}
|
||||
|
||||
private fun genProperty(
|
||||
id: String,
|
||||
receiverType: KotlinType,
|
||||
type: KotlinType,
|
||||
containingDeclaration: DeclarationDescriptor,
|
||||
sourceElement: SourceElement,
|
||||
context: SyntheticElementResolveContext,
|
||||
errorType: String?
|
||||
): PropertyDescriptor {
|
||||
val alwaysCastToView = type.constructor.declarationDescriptor?.fqNameUnsafe?.asString() == AndroidConst.VIEWSTUB_FQNAME
|
||||
|
||||
val property = object : AndroidSyntheticProperty, PropertyDescriptorImpl(
|
||||
containingDeclaration,
|
||||
null,
|
||||
Annotations.EMPTY,
|
||||
Modality.FINAL,
|
||||
Visibilities.PUBLIC,
|
||||
false,
|
||||
Name.identifier(id),
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
sourceElement,
|
||||
false,
|
||||
false) {
|
||||
override val errorType = errorType
|
||||
override val alwaysCastToView = alwaysCastToView
|
||||
}
|
||||
|
||||
val actualType = if (alwaysCastToView) context.viewType else type
|
||||
val flexibleType = DelegatingFlexibleType.create(actualType, actualType.makeNullable(), FlexibleTypeCapabilities.NONE)
|
||||
property.setType(
|
||||
flexibleType,
|
||||
emptyList<TypeParameterDescriptor>(),
|
||||
null,
|
||||
receiverType)
|
||||
|
||||
val getter = PropertyGetterDescriptorImpl(
|
||||
property,
|
||||
Annotations.EMPTY,
|
||||
Modality.FINAL,
|
||||
Visibilities.PUBLIC,
|
||||
false,
|
||||
false,
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
null,
|
||||
SourceElement.NO_SOURCE
|
||||
)
|
||||
|
||||
getter.initialize(null)
|
||||
|
||||
property.initialize(getter, null)
|
||||
|
||||
return property
|
||||
}
|
||||
|
||||
interface AndroidSyntheticFunction
|
||||
|
||||
interface AndroidSyntheticProperty {
|
||||
val errorType: String?
|
||||
val alwaysCastToView: Boolean
|
||||
|
||||
val isErrorType: Boolean
|
||||
get() = errorType != null
|
||||
}
|
||||
Reference in New Issue
Block a user