diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 2d086925a01..f0b4d4a071e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -2324,6 +2324,15 @@ public class ExpressionCodegen extends KtVisitor impleme } } + Collection codegenExtensions = ExpressionCodegenExtension.Companion.getInstances(state.getProject()); + if (!codegenExtensions.isEmpty()) { + ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v); + for (ExpressionCodegenExtension extension : codegenExtensions) { + StackValue stackValue = extension.applyFunction(receiver, resolvedCall, context); + if (stackValue != null) return stackValue; + } + } + Callable callable = resolveToCallable(fd, superCall, resolvedCall); return callable.invokeMethodWithArguments(resolvedCall, receiver, this); @@ -2385,15 +2394,6 @@ public class ExpressionCodegen extends KtVisitor impleme if (!(resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor)) { // otherwise already receiver = StackValue.receiver(resolvedCall, receiver, this, callableMethod); - - Collection codegenExtensions = ExpressionCodegenExtension.Companion.getInstances(state.getProject()); - if (!codegenExtensions.isEmpty()) { - ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v); - for (ExpressionCodegenExtension extension : codegenExtensions) { - if (extension.applyFunction(receiver, resolvedCall, context)) return; - } - } - receiver.put(receiver.type, v); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/extensions/ExpressionCodegenExtension.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/extensions/ExpressionCodegenExtension.kt index 0ae88696f99..277bf1d562c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/extensions/ExpressionCodegenExtension.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/extensions/ExpressionCodegenExtension.kt @@ -25,10 +25,10 @@ import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.resolve.* public interface ExpressionCodegenExtension { - companion object : ProjectExtensionDescriptor("org.jetbrains.kotlin.expressionCodegenExtension", javaClass()) + companion object : ProjectExtensionDescriptor( + "org.jetbrains.kotlin.expressionCodegenExtension", ExpressionCodegenExtension::class.java) public class Context( public val typeMapper: JetTypeMapper, @@ -42,10 +42,10 @@ public interface ExpressionCodegenExtension { public fun applyProperty(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): StackValue? = null /** - * Used for generating custom byte code for the function call. This function has non-lazy semantics. - * Returns true if the stack was modified. + * Used for generating custom byte code for the function call. This function has lazy semantics. + * Returns new stack value. */ - public fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): Boolean = false + public fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): StackValue? = null public fun generateClassSyntheticParts( classBuilder: ClassBuilder, diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt index 769f70eed0b..0ab46c97123 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt @@ -83,6 +83,7 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension +import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService import org.jetbrains.kotlin.utils.PathUtil @@ -139,6 +140,7 @@ public class KotlinCoreEnvironment private constructor( ExpressionCodegenExtension.registerExtensionPoint(project) ClassBuilderInterceptorExtension.registerExtensionPoint(project) AnalysisCompletedHandlerExtension.registerExtensionPoint(project) + PackageFragmentProviderExtension.registerExtensionPoint(project) StorageComponentContainerContributor.registerExtensionPoint(project) for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAnalyzerFacade.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAnalyzerFacade.kt index 9248c1b1e8b..f113ea15342 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAnalyzerFacade.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAnalyzerFacade.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer +import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension import org.jetbrains.kotlin.resolve.TargetEnvironment import org.jetbrains.kotlin.resolve.TargetPlatform import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform @@ -77,9 +78,10 @@ public object JvmAnalyzerFacade : AnalyzerFacade() { resolverForModule.componentProvider.get() } + val trace = CodeAnalyzerInitializer.getInstance(project).createTrace() val container = createContainerForLazyResolveWithJava( moduleContext, - CodeAnalyzerInitializer.getInstance(project).createTrace(), + trace, declarationProviderFactory, moduleContentScope, moduleClassResolver, @@ -89,7 +91,14 @@ public object JvmAnalyzerFacade : AnalyzerFacade() { val resolveSession = container.get() val javaDescriptorResolver = container.get() - val providersForModule = listOf(resolveSession.getPackageFragmentProvider(), javaDescriptorResolver.packageFragmentProvider) + val providersForModule = arrayListOf( + resolveSession.packageFragmentProvider, + javaDescriptorResolver.packageFragmentProvider) + + providersForModule += PackageFragmentProviderExtension.getInstances(project) + .map { it.getPackageFragmentProvider(project, moduleDescriptor, moduleContext.storageManager, trace, moduleInfo) } + .filterNotNull() + return ResolverForModule(CompositePackageFragmentProvider(providersForModule), container) } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/TopDownAnalyzerFacadeForJVM.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/TopDownAnalyzerFacadeForJVM.java index bf0eade3c5f..07365e2cda8 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/TopDownAnalyzerFacadeForJVM.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/TopDownAnalyzerFacadeForJVM.java @@ -43,6 +43,7 @@ import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.TopDownAnalysisMode; import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension; +import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension; import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform; import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory; @@ -135,6 +136,12 @@ public enum TopDownAnalyzerFacadeForJVM { } additionalProviders.add(container.getJavaDescriptorResolver().getPackageFragmentProvider()); + for (PackageFragmentProviderExtension extension : PackageFragmentProviderExtension.Companion.getInstances(project)) { + PackageFragmentProvider provider = extension.getPackageFragmentProvider( + project, moduleContext.getModule(), moduleContext.getStorageManager(), trace, null); + if (provider != null) additionalProviders.add(provider); + } + container.getLazyTopDownAnalyzerForTopLevel().analyzeFiles(topDownAnalysisMode, allFiles, additionalProviders); BindingContext bindingContext = trace.getBindingContext(); diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/extensions/PackageFragmentProviderExtension.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/extensions/PackageFragmentProviderExtension.kt new file mode 100644 index 00000000000..280e76918d8 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/extensions/PackageFragmentProviderExtension.kt @@ -0,0 +1,39 @@ +/* + * 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.resolve.jvm.extensions + +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.analyzer.ModuleInfo +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.storage.StorageManager + +public interface PackageFragmentProviderExtension { + companion object : ProjectExtensionDescriptor( + "org.jetbrains.kotlin.packageFragmentProviderExtension", + PackageFragmentProviderExtension::class.java + ) + + public fun getPackageFragmentProvider( + project: Project, + module: ModuleDescriptor, + storageManager: StorageManager, + trace: BindingTrace, + moduleInfo: ModuleInfo? + ): PackageFragmentProvider? +} diff --git a/plugins/android-compiler-plugin/android-compiler-plugin.iml b/plugins/android-compiler-plugin/android-compiler-plugin.iml index 931e16a64d9..8334b9db396 100644 --- a/plugins/android-compiler-plugin/android-compiler-plugin.iml +++ b/plugins/android-compiler-plugin/android-compiler-plugin.iml @@ -10,7 +10,7 @@ - + diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt index 8f42aa37c7b..bf8c1eb4ab0 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt @@ -17,10 +17,7 @@ package org.jetbrains.kotlin.android.synthetic import com.intellij.mock.MockProject -import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.extensions.Extensions -import com.intellij.openapi.project.Project -import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension import org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension import org.jetbrains.kotlin.android.synthetic.diagnostic.AndroidExtensionPropertiesCallChecker @@ -37,10 +34,9 @@ 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.ExternalDeclarationsProvider import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor -import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.TargetPlatform +import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform public object AndroidConfigurationKeys { @@ -73,13 +69,6 @@ public class AndroidCommandLineProcessor : CommandLineProcessor { } } -public class CliAndroidDeclarationsProvider(private val project: Project) : ExternalDeclarationsProvider { - override fun getExternalDeclarations(moduleInfo: ModuleInfo?): Collection { - val parser = ServiceManager.getService(project, SyntheticFileGenerator::class.java) as? CliSyntheticFileGenerator - return parser?.getSyntheticFiles() ?: listOf() - } -} - public class AndroidComponentRegistrar : ComponentRegistrar { public override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) { @@ -87,18 +76,14 @@ public class AndroidComponentRegistrar : ComponentRegistrar { val variants = configuration.get(AndroidConfigurationKeys.VARIANT)?.map { parseVariant(it) }?.filterNotNull() ?: emptyList() if (variants.isNotEmpty() && !applicationPackage.isNullOrBlank()) { - val xmlProcessor = CliSyntheticFileGenerator(project, applicationPackage!!, variants) - - project.registerService(SyntheticFileGenerator::class.java, xmlProcessor) - - val layoutXmlFileManager = CliAndroidLayoutXmlFileManager(project, applicationPackage, variants) + val layoutXmlFileManager = CliAndroidLayoutXmlFileManager(project, applicationPackage!!, variants) project.registerService(AndroidLayoutXmlFileManager::class.java, layoutXmlFileManager) - ExternalDeclarationsProvider.registerExtension(project, CliAndroidDeclarationsProvider(project)) 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()) } } diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidConst.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidConst.kt index 0994f7d01df..fe1b4fafb19 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidConst.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidConst.kt @@ -16,20 +16,19 @@ package org.jetbrains.kotlin.android.synthetic -import com.intellij.openapi.util.Key import org.jetbrains.kotlin.lexer.KtKeywordToken import org.jetbrains.kotlin.lexer.KtTokens public object AndroidConst { - val ANDROID_USER_PACKAGE: Key = Key.create("ANDROID_USER_PACKAGE") - - val SYNTHETIC_FILENAME_PREFIX: String = "ANDROIDXML_" - val LAYOUT_POSTFIX: String = "_LAYOUT" - val VIEW_LAYOUT_POSTFIX: String = "_VIEW" - val SYNTHETIC_PACKAGE: String = "kotlinx.android.synthetic" val SYNTHETIC_PACKAGE_PATH_LENGTH = SYNTHETIC_PACKAGE.count { it == '.' } + 1 + val SYNTHETIC_SUBPACKAGES: List = SYNTHETIC_PACKAGE.split('.').fold(arrayListOf()) { 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" @@ -41,6 +40,9 @@ public object AndroidConst { val CLEAR_FUNCTION_NAME = "clearFindViewByIdCache" + + //TODO FqName / ClassId + val VIEW_FQNAME = "android.view.View" val ACTIVITY_FQNAME = "android.app.Activity" val FRAGMENT_FQNAME = "android.app.Fragment" @@ -56,8 +58,6 @@ public object AndroidConst { val FQNAME_RESOLVE_PACKAGES = listOf("android.widget", "android.webkit", "android.view") } -public fun nameToIdDeclaration(name: String): String = AndroidConst.ID_DECLARATION_PREFIX + name - public fun idToName(id: String): String? { for (prefix in AndroidConst.XML_ID_PREFIXES) { if (id.startsWith(prefix)) return escapeAndroidIdentifier(id.replace(prefix, "")) @@ -71,4 +71,13 @@ public fun isWidgetTypeIgnored(xmlType: String): Boolean { fun escapeAndroidIdentifier(id: String): String { return if (id in AndroidConst.ESCAPED_IDENTIFIERS) "`$id`" else id -} \ No newline at end of file +} + +internal fun List.forEachUntilLast(operation: (T) -> Unit) { + val lastIndex = lastIndex + forEachIndexed { i, t -> + if (i < lastIndex) { + operation(t) + } + } +} diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/Context.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/Context.kt deleted file mode 100644 index bf6c7890ff9..00000000000 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/Context.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 java.util.ArrayList - -open class Context(val buffer: StringBuffer = StringBuffer(), private var indentDepth: Int = 0) { - open class InvalidIndent(num: Int) : RuntimeException("Indentation level < 0: $num") - - val indentUnit = " " - protected var currentIndent: String = indentUnit.repeat(indentDepth) - private val children = ArrayList() - - public fun incIndent() { - indentDepth++ - currentIndent += indentUnit - } - - public fun decIndent() { - indentDepth-- - if (indentDepth < 0) - throw InvalidIndent(indentDepth) - currentIndent = currentIndent.substring(0, currentIndent.length - indentUnit.length) - } - - public open fun write(what: String) { - writeNoIndent(currentIndent) - writeNoIndent(what) - } - - public fun writeNoIndent(what: String) { - buffer.append(what) - } - - public fun writeln(what: String) { - write(what) - newLine() - } - - public fun newLine() { - writeNoIndent("\n") - } - - - public fun trim(num: Int) { - buffer.delete(buffer.length - num, buffer.length()) - } - - public fun fork(newBuffer: StringBuffer = StringBuffer(), - newIndentDepth: Int = indentDepth): Context { - val child = Context(newBuffer, newIndentDepth) - children.add(child) - return child - } - - public fun absorbChildren(noIndent: Boolean = true) { - for (child in children) { - child.absorbChildren() - if (noIndent) - writeNoIndent(child.toString()) - else - write(child.toString()) - } - children.clear() - } - - public override fun toString(): String { - return buffer.toString() - } -} - diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/KotlinWriter.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/KotlinWriter.kt deleted file mode 100644 index f4840911913..00000000000 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/KotlinWriter.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 - -interface KotlinWriter { - fun toStringBuffer(): StringBuffer -} - -class KotlinStringWriter : KotlinWriter { - - private val ctx = Context() - private val imports = ctx.fork() - private val body = ctx.fork() - - fun writeImmutableProperty(name: String, - retType: String, - getterBody: Collection) { - body.writeln("val $name: $retType") - body.incIndent() - body.write("get() ") - if (getterBody.size > 1) { - body.writeNoIndent("{\n") - body.incIndent() - for (stmt in getterBody) { - body.writeln(stmt) - } - body.decIndent() - body.writeln("}") - } - else { - body.writeNoIndent("=") - body.writeNoIndent(getterBody.joinToString("").replace("return", "")) - body.newLine() - } - body.decIndent() - body.newLine() - } - - fun writeImmutableExtensionProperty(receiver: String, - name: String, - retType: String, - getterBody: Collection) { - writeImmutableProperty("$receiver.$name", retType, getterBody) - } - - fun writeImport(what: String) { - imports.writeln("import $what") - } - - fun writePackage(_package: String) { - ctx.writeln("package $_package\n") - } - - fun writeEmptyLine() { - body.newLine() - } - - fun writeText(text: String) { - body.writeln(text) - } - - override fun toStringBuffer(): StringBuffer { - ctx.absorbChildren() - return ctx.buffer - } - - override fun toString(): String { - return ctx.buffer.toString() - } -} - diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt index 4c9f531fdce..2670ac27ac1 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt @@ -17,6 +17,9 @@ 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 @@ -26,7 +29,6 @@ import org.jetbrains.kotlin.codegen.state.JetTypeMapper 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.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny @@ -109,12 +111,12 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { else null } - override fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): Boolean { + 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 false + else null } private fun generateSyntheticFunctionCall( @@ -122,22 +124,22 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context, descriptor: FunctionDescriptor - ): Boolean { - if (descriptor.getAndroidPackage() == null) return false - if (descriptor.name.asString() != AndroidConst.CLEAR_FUNCTION_NAME) return false + ): StackValue? { + if (descriptor !is AndroidSyntheticFunction) return null + if (descriptor.name.asString() != AndroidConst.CLEAR_FUNCTION_NAME) return null - val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return false - if (!isCacheSupported(declarationDescriptor)) return true + val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return null + if (!isCacheSupported(declarationDescriptor)) return StackValue.functionCall(Type.VOID_TYPE) {} val androidClassType = AndroidClassType.getClassType(declarationDescriptor) - if (androidClassType == AndroidClassType.UNKNOWN) return false + if (androidClassType == AndroidClassType.UNKNOWN) return null - val bytecodeClassName = c.typeMapper.mapType(declarationDescriptor).internalName + return StackValue.functionCall(Type.VOID_TYPE) { + val bytecodeClassName = c.typeMapper.mapType(declarationDescriptor).internalName - receiver.put(c.typeMapper.mapType(declarationDescriptor), c.v) - c.v.invokevirtual(bytecodeClassName, CLEAR_CACHE_METHOD_NAME, "()V", false) - - return true + receiver.put(c.typeMapper.mapType(declarationDescriptor), it) + it.invokevirtual(bytecodeClassName, CLEAR_CACHE_METHOD_NAME, "()V", false) + } } private fun generateSyntheticPropertyCall( @@ -146,7 +148,9 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { c: ExpressionCodegenExtension.Context, descriptor: PropertyDescriptor ): StackValue? { - val androidPackage = descriptor.getAndroidPackage() ?: return null + if (descriptor !is AndroidSyntheticProperty) return null + val packageFragment = descriptor.containingDeclaration as? AndroidSyntheticPackageFragmentDescriptor ?: return null + val androidPackage = packageFragment.packageData.moduleData.module.applicationPackage val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return null val androidClassType = AndroidClassType.getClassType(declarationDescriptor) @@ -224,10 +228,6 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { } } - private fun CallableDescriptor.getAndroidPackage(): String? { - return DescriptorToSourceUtils.getContainingFile(this)?.getUserData(AndroidConst.ANDROID_USER_PACKAGE) - } - private fun ResolvedCall<*>.getReceiverDeclarationDescriptor(): ClassifierDescriptor? { return (extensionReceiver as ReceiverValue).type.constructor.declarationDescriptor } diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt new file mode 100644 index 00000000000..a5bcd230ded --- /dev/null +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt @@ -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) + +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(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) + } + } + } + } + + 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) + } + } +} diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/descriptors/LazySyntheticElementResolveContext.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/descriptors/LazySyntheticElementResolveContext.kt new file mode 100644 index 00000000000..b8aef90c577 --- /dev/null +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/descriptors/LazySyntheticElementResolveContext.kt @@ -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(3) + receivers += activityType + fragmentType?.let { receivers += it } + supportFragmentType?.let { receivers += it } + receivers + } + + val fragmentTypes: List> by lazy { + if (fragmentType == null) { + emptyList>() + } + else { + val types = ArrayList>(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 { + if (forView) return listOf(viewType) + return widgetReceivers + } +} diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/descriptors/PredefinedPackageFragmentDescriptor.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/descriptors/PredefinedPackageFragmentDescriptor.kt new file mode 100644 index 00000000000..acda244491a --- /dev/null +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/descriptors/PredefinedPackageFragmentDescriptor.kt @@ -0,0 +1,61 @@ +/* + * 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.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +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.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, + private val descriptors: (PredefinedPackageFragmentDescriptor) -> Collection = { 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 + + @Suppress("UNCHECKED_CAST") + override fun getContributedFunctions(name: Name, location: LookupLocation) = + calculatedDescriptors().filter { it is FunctionDescriptor && it.name == name } as List + + 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) + } + } +} diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/diagnostic/AndroidExtensionPropertiesCallChecked.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/diagnostic/AndroidExtensionPropertiesCallChecked.kt index 4dfe6216892..5013665ccb0 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/diagnostic/AndroidExtensionPropertiesCallChecked.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/diagnostic/AndroidExtensionPropertiesCallChecked.kt @@ -16,32 +16,42 @@ package org.jetbrains.kotlin.android.synthetic.diagnostic -import org.jetbrains.kotlin.android.synthetic.AndroidConst -import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator 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.resolve.constants.StringValue 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 public class AndroidExtensionPropertiesCallChecker : CallChecker { override fun check(resolvedCall: ResolvedCall, context: BasicCallResolutionContext) { val expression = context.call.calleeExpression ?: return val propertyDescriptor = resolvedCall.resultingDescriptor as? PropertyDescriptor ?: return - val syntheticPackage = propertyDescriptor.containingDeclaration as? PackageFragmentDescriptor ?: return - if (!syntheticPackage.fqName.asString().startsWith("${AndroidConst.SYNTHETIC_PACKAGE}.")) return + val containingPackage = propertyDescriptor.containingDeclaration as? AndroidSyntheticPackageFragmentDescriptor ?: return + val androidSyntheticProperty = propertyDescriptor as? AndroidSyntheticProperty ?: return - val invalidWidgetTypeAnnotation = propertyDescriptor.annotations.findAnnotation( - SyntheticFileGenerator.INVALID_WIDGET_TYPE_ANNOTATION_FQNAME) ?: return - - val type = invalidWidgetTypeAnnotation.allValueArguments.filterKeys { - it.name.asString() == SyntheticFileGenerator.INVALID_WIDGET_TYPE_ANNOTATION_TYPE_PARAMETER - }.values().firstOrNull() as? StringValue ?: return - - val erroneousType = type.value - val warning = if (erroneousType.contains('.')) SYNTHETIC_UNRESOLVED_WIDGET_TYPE else SYNTHETIC_INVALID_WIDGET_TYPE - context.trace.report(warning.on(expression, erroneousType)) + 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)) + } + } \ No newline at end of file diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/diagnostic/DefaultErrorMessagesAndroid.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/diagnostic/DefaultErrorMessagesAndroid.kt index 1dd4ce7be4d..1118af3f2e2 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/diagnostic/DefaultErrorMessagesAndroid.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/diagnostic/DefaultErrorMessagesAndroid.kt @@ -33,6 +33,9 @@ public class DefaultErrorMessagesAndroid : DefaultErrorMessages.Extension { 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") } } diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/diagnostic/ErrorsAndroid.java b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/diagnostic/ErrorsAndroid.java index f1eee3b9b3b..273997b0e3f 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/diagnostic/ErrorsAndroid.java +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/diagnostic/ErrorsAndroid.java @@ -16,6 +16,7 @@ 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; @@ -25,6 +26,7 @@ import static org.jetbrains.kotlin.diagnostics.Severity.WARNING; public interface ErrorsAndroid { DiagnosticFactory1 SYNTHETIC_INVALID_WIDGET_TYPE = DiagnosticFactory1.create(WARNING); DiagnosticFactory1 SYNTHETIC_UNRESOLVED_WIDGET_TYPE = DiagnosticFactory1.create(WARNING); + DiagnosticFactory0 SYNTHETIC_DEPRECATED_PACKAGE = DiagnosticFactory0.create(WARNING); @SuppressWarnings("UnusedDeclaration") Object _initializer = new Object() { diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/AndroidLayoutXmlFileManager.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/AndroidLayoutXmlFileManager.kt index 14e5ed15cd9..fcd35a6afcf 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/AndroidLayoutXmlFileManager.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/AndroidLayoutXmlFileManager.kt @@ -24,6 +24,9 @@ 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.* @@ -38,9 +41,9 @@ abstract class AndroidLayoutXmlFileManager(val project: Project) { public abstract val androidModule: AndroidModule? - public open fun propertyToXmlAttributes(property: KtProperty): List = listOf() + public open fun propertyToXmlAttributes(propertyDescriptor: PropertyDescriptor): List = listOf() - public fun getLayoutXmlFiles(): AndroidModuleData { + open fun getModuleData(): AndroidModuleData { val androidModule = androidModule ?: return AndroidModuleData.EMPTY return AndroidModuleData(androidModule, androidModule.variants.map { getVariantData(it) }) } @@ -82,6 +85,45 @@ abstract class AndroidLayoutXmlFileManager(val project: Project) { return AndroidVariantData(variant, layoutNameToXmlFiles) } + fun extractResources(files: List, module: ModuleDescriptor): List { + return filterDuplicates(doExtractResources(files, module)) + } + + protected abstract fun doExtractResources(files: List, module: ModuleDescriptor): List + + 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): List { + val resourceMap = linkedMapOf() + val resourcesToExclude = hashSetOf() + + 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 { public fun getInstance(module: Module): AndroidLayoutXmlFileManager? { val service = ModuleServiceManager.getService(module, AndroidLayoutXmlFileManager::class.java) diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt new file mode 100644 index 00000000000..e55edbdfddf --- /dev/null +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt @@ -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.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? + + private val cache = WeakHashMap() + + 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 cachedPackageFragmentProvider = cache[moduleData] + if (cachedPackageFragmentProvider != null) return cachedPackageFragmentProvider + + val lazyContext = LazySyntheticElementResolveContext(module, storageManager) + + val packageDescriptors = arrayListOf() + + // Packages with synthetic properties + for (variantData in moduleData) { + for ((layoutName, layouts) in variantData) { + fun createPackageFragment(fqName: String, forView: Boolean, isDeprecated: Boolean = false): PackageFragmentDescriptor { + val resources = layoutXmlFileManager.extractResources(layouts, module) + val packageData = AndroidSyntheticPackageData(moduleData, forView, isDeprecated, resources) + return AndroidSyntheticPackageFragmentDescriptor(module, FqName(fqName), packageData, lazyContext, storageManager) + } + + val packageFqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name + '.' + layoutName + + packageDescriptors += createPackageFragment(packageFqName, false) + packageDescriptors += createPackageFragment(packageFqName + ".view", true) + + if (variantData.variant.isMainVariant && !moduleData.any { it.variant.name == layoutName }) { + val deprecatedPackageFqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + layoutName + packageDescriptors += createPackageFragment(deprecatedPackageFqName, false, isDeprecated = true) + packageDescriptors += createPackageFragment(deprecatedPackageFqName + ".view", true, isDeprecated = true) + } + } + } + + // Empty middle packages + AndroidConst.SYNTHETIC_SUBPACKAGES.forEachUntilLast { s -> + packageDescriptors += PredefinedPackageFragmentDescriptor(s, module, storageManager) + } + for (variantData in moduleData) { + val fqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name + packageDescriptors += PredefinedPackageFragmentDescriptor(fqName, module, storageManager) + } + + // Package with clearFindViewByIdCache() + AndroidConst.SYNTHETIC_SUBPACKAGES.last().let { s -> + packageDescriptors += PredefinedPackageFragmentDescriptor(s, module, storageManager) { descriptor -> + lazyContext().getWidgetReceivers(false).map { genClearCacheFunction(descriptor, it) } + } + } + + val provider = AndroidSyntheticPackageFragmentProvider(allPackageDescriptors) + cache[moduleData] = provider + return provider + } +} + +class AndroidSyntheticPackageFragmentProvider(val packageFragments: Collection) : 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() +} diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliAndroidLayoutXmlFileManager.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliAndroidLayoutXmlFileManager.kt index 29be4a2b0ab..743744e6d8c 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliAndroidLayoutXmlFileManager.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliAndroidLayoutXmlFileManager.kt @@ -16,7 +16,12 @@ 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 @@ -25,10 +30,32 @@ public class CliAndroidLayoutXmlFileManager( private val applicationPackage: String, private val variants: List ) : AndroidLayoutXmlFileManager(project) { + private companion object { + val LOG = Logger.getInstance(CliAndroidLayoutXmlFileManager::class.java) + } - override val androidModule by lazy { AndroidModule(applicationPackage, variants) } + override val androidModule = AndroidModule(applicationPackage, variants) - val saxParser: SAXParser = initSAX() + private val saxParser: SAXParser = initSAX() + + override fun doExtractResources(files: List, module: ModuleDescriptor): List { + val resources = arrayListOf() + + 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() diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidUtil.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliAndroidPackageFragmentProviderExtension.kt similarity index 52% rename from plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidUtil.kt rename to plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliAndroidPackageFragmentProviderExtension.kt index 811146539a7..2178d2b75f2 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidUtil.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliAndroidPackageFragmentProviderExtension.kt @@ -14,19 +14,14 @@ * limitations under the License. */ -package org.jetbrains.kotlin.android.synthetic +package org.jetbrains.kotlin.android.synthetic.res -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.util.Computable -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.analyzer.ModuleInfo -fun isAndroidSyntheticFile(f: PsiFile?): Boolean { - return f?.getUserData(AndroidConst.ANDROID_USER_PACKAGE) != null -} - -public fun isAndroidSyntheticElement(element: PsiElement?): Boolean { - return isAndroidSyntheticFile(ApplicationManager.getApplication().runReadAction(Computable { - element?.containingFile - })) -} +class CliAndroidPackageFragmentProviderExtension : AndroidPackageFragmentProviderExtension() { + override fun getLayoutXmlFileManager(project: Project, moduleInfo: ModuleInfo?): AndroidLayoutXmlFileManager? { + return ServiceManager.getService(project, AndroidLayoutXmlFileManager::class.java) + } +} \ No newline at end of file diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliSyntheticFileGenerator.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliSyntheticFileGenerator.kt deleted file mode 100644 index e327c9b42f1..00000000000 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliSyntheticFileGenerator.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.PsiElementFinder -import com.intellij.psi.PsiFile -import com.intellij.psi.impl.PsiElementFinderImpl -import com.intellij.psi.search.GlobalSearchScope -import org.jetbrains.kotlin.android.synthetic.AndroidConst -import org.jetbrains.kotlin.android.synthetic.AndroidXmlHandler -import org.jetbrains.kotlin.psi.KtFile -import java.io.ByteArrayInputStream - -public open class CliSyntheticFileGenerator( - project: Project, - private val manifestPath: String, - private val variants: List -) : SyntheticFileGenerator(project) { - - private val cachedJetFiles by lazy { - val supportV4 = supportV4Available() - - generateSyntheticJetFiles(generateSyntheticFiles(true, supportV4)) - } - - override val layoutXmlFileManager: CliAndroidLayoutXmlFileManager by lazy { - CliAndroidLayoutXmlFileManager(project, manifestPath, variants) - } - - public override fun getSyntheticFiles(): List = cachedJetFiles - - override fun extractLayoutResources(files: List): List { - val resources = arrayListOf() - - val handler = AndroidXmlHandler { id, tag -> - resources += parseAndroidResource(id, tag) { tag -> - resolveFqClassNameForView(tag) - } - } - - for (file in files) { - try { - val inputStream = ByteArrayInputStream(file.virtualFile.contentsToByteArray()) - layoutXmlFileManager.saxParser.parse(inputStream, handler) - } catch (e: Throwable) { - LOG.error(e) - } - } - - return filterDuplicates(resources) - } - - override fun checkIfClassExist(fqName: String): Boolean { - val scope = GlobalSearchScope.allScope(project) - val psiElementFinders = project.getExtensions(PsiElementFinder.EP_NAME).filter { it is PsiElementFinderImpl } - - for (finder in psiElementFinders) { - val clazz = finder.findClass(fqName, scope) - if (clazz != null) return true - } - return false - } - - override fun parseAndroidWidget(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource { - val fqName = fqNameResolver(tag) - val invalidType = if (fqName != null) null else tag - val type = fqName ?: (if ('.' in tag) tag else AndroidConst.VIEW_FQNAME) - return AndroidWidget(id, type, invalidType) - } - - private companion object { - private val LOG: Logger = Logger.getInstance(CliSyntheticFileGenerator::class.java) - } -} - diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/SyntheticFileGenerator.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/SyntheticFileGenerator.kt deleted file mode 100644 index d51addbd798..00000000000 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/SyntheticFileGenerator.kt +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 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.PsiFile -import com.intellij.psi.PsiManager -import com.intellij.psi.util.CachedValue -import com.intellij.psi.util.CachedValueProvider.Result -import com.intellij.psi.util.CachedValuesManager -import com.intellij.testFramework.LightVirtualFile -import org.jetbrains.kotlin.android.synthetic.AndroidConst -import org.jetbrains.kotlin.android.synthetic.KotlinStringWriter -import org.jetbrains.kotlin.android.synthetic.escapeAndroidIdentifier -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.types.Flexibility - -public class AndroidSyntheticFile(val name: String, val contents: String) { - companion object { - fun create(name: String, vararg contents: String) = AndroidSyntheticFile(name, contents.joinToString("\n\n")) - } -} - -public abstract class SyntheticFileGenerator(protected val project: Project) { - - public class NoAndroidManifestFound : Exception("No android manifest file found in project root") - - public abstract val layoutXmlFileManager: AndroidLayoutXmlFileManager - - public abstract fun getSyntheticFiles(): List - - protected open fun generateSyntheticFiles(generateCommonFiles: Boolean, supportV4: Boolean): List { - val commonFiles = if (generateCommonFiles) generateCommonFiles(supportV4) else listOf() - - return layoutXmlFileManager.getLayoutXmlFiles().flatMap { variantData -> - variantData.flatMap { entry -> - val files = entry.value - val resources = extractLayoutResources(files) - - val layoutName = entry.key - - arrayListOf().apply { - this += renderMainLayoutFiles(variantData.variant, layoutName, resources, supportV4) - this += renderViewLayoutFiles(variantData.variant, layoutName, resources) - } - } - }.filterNotNull() + commonFiles - } - - private fun generateCommonFiles(supportV4: Boolean): List { - val renderSyntheticFile = renderSyntheticFile("clearCache") { - writePackage(AndroidConst.SYNTHETIC_PACKAGE) - writeAndroidImports() - writeClearCacheFunction(AndroidConst.ACTIVITY_FQNAME) - writeClearCacheFunction(AndroidConst.FRAGMENT_FQNAME) - if (supportV4) { - writeClearCacheFunction(AndroidConst.SUPPORT_FRAGMENT_FQNAME) - } - } - val clearCacheFile = renderSyntheticFile - - return listOf(clearCacheFile, TOOLS_FILE) - } - - protected abstract fun extractLayoutResources(files: List): List - - protected abstract fun checkIfClassExist(fqName: String): Boolean - - private fun renderMainLayoutFiles( - variant: AndroidVariant, - layoutName: String, - resources: List, - supportV4: Boolean - ): List { - return renderLayoutFile(variant, layoutName + AndroidConst.LAYOUT_POSTFIX, escapeAndroidIdentifier(layoutName), resources) { - val properties = it.mainProperties.toArrayList() - if (supportV4) properties.addAll(it.mainPropertiesForSupportV4) - properties - } - } - - private fun renderViewLayoutFiles( - variant: AndroidVariant, - layoutName: String, - resources: List - ): List { - return renderLayoutFile(variant, layoutName + AndroidConst.VIEW_LAYOUT_POSTFIX, - escapeAndroidIdentifier(layoutName) + ".view", resources) { it.viewProperties } - } - - private fun renderLayoutFile( - variant: AndroidVariant, - filename: String, - layoutName: String, - resources: List, - properties: (AndroidResource) -> List>): List { - fun render(defaultVariant: Boolean = false): AndroidSyntheticFile { - val fullFilename = (if (defaultVariant) "" else "${variant.name}_") + filename - return renderSyntheticFile(fullFilename) { - val packageName = if (defaultVariant) - AndroidConst.SYNTHETIC_PACKAGE + "." + layoutName - else - AndroidConst.SYNTHETIC_PACKAGE + '.' + variant.name + '.' + layoutName - - writePackage(packageName) - writeAndroidImports() - - for (res in resources) { - properties(res).forEach { property -> - if (defaultVariant) { - val deprecatedText = "Use the property from the 'main' variant instead" - val import = AndroidConst.SYNTHETIC_PACKAGE + '.' + variant.name + '.' + layoutName + '.' + res.id - writeText("@Deprecated(\"$deprecatedText\", ReplaceWith(\"${res.id}\", \"$import\"))") - } - writeSyntheticProperty(property.first, res, property.second) - } - } - } - } - - return if (variant.isMainVariant) listOf(render(), render(true)) else listOf(render()) - } - - private fun renderSyntheticFile(filename: String, init: KotlinStringWriter.() -> Unit): AndroidSyntheticFile { - val stringWriter = KotlinStringWriter() - stringWriter.init() - return AndroidSyntheticFile(filename, stringWriter.toStringBuffer().toString()) - } - - private fun KotlinStringWriter.writeAndroidImports() { - ANDROID_IMPORTS.forEach { writeImport(it) } - writeEmptyLine() - } - - private fun KotlinStringWriter.writeSyntheticProperty(receiver: String, resource: AndroidResource, stubCall: String) { - val className = if (isFromSupportV4Package(receiver)) resource.supportClassName else resource.className - val cast = if (resource.className != AndroidConst.VIEW_FQNAME) " as? $className" else "" - val body = arrayListOf("return $stubCall$cast") - - // Annotation on wrong widget type - if (resource is AndroidWidget && resource.invalidType != null) { - writeText("@${INVALID_WIDGET_TYPE_ANNOTATION_FQNAME.asString()}(\"${resource.invalidType}\")") - } - - writeImmutableExtensionProperty(receiver, - name = resource.id, - retType = "$EXPLICIT_FLEXIBLE_CLASS_NAME<$className, $className?>", - getterBody = body) - } - - private fun KotlinStringWriter.writeClearCacheFunction(receiver: String) { - writeText("public fun $receiver.${AndroidConst.CLEAR_FUNCTION_NAME}() {}\n") - } - - protected fun cachedValue(result: () -> Result): CachedValue { - return CachedValuesManager.getManager(project).createCachedValue(result, false) - } - - private fun isFromSupportV4Package(fqName: String): Boolean { - return fqName.startsWith(AndroidConst.SUPPORT_V4_PACKAGE) - } - - protected fun resolveFqClassNameForView(tag: String): String? { - if (tag.contains('.')) { - if (!checkIfClassExist(tag)) { - return null - } - return tag - } - for (pkg in AndroidConst.FQNAME_RESOLVE_PACKAGES) { - val fqName = "$pkg.$tag" - if (checkIfClassExist(fqName)) { - return fqName - } - } - return null - } - - protected fun parseAndroidResource(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource { - return when (tag) { - "fragment" -> AndroidFragment(id) - "include" -> AndroidWidget(id, AndroidConst.VIEW_FQNAME) - else -> parseAndroidWidget(id, tag, fqNameResolver) - } - } - - protected abstract fun parseAndroidWidget(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource - - protected fun supportV4Available(): Boolean = checkIfClassExist(AndroidConst.SUPPORT_FRAGMENT_FQNAME) - - protected fun filterDuplicates(resources: List): List { - val resourceMap = linkedMapOf() - val resourcesToExclude = hashSetOf() - - 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 AndroidWidget && existing.className != res.className && existing.className != AndroidConst.VIEW_FQNAME) { - // Widgets with the same id but different types exist. - resourceMap.put(res.id, AndroidWidget(res.id, AndroidConst.VIEW_FQNAME)) - } - } - else resourceMap.put(res.id, res) - } - resourcesToExclude.forEach { resourceMap.remove(it) } - return resourceMap.values().toList() - } - - protected fun generateSyntheticJetFiles(files: List): List { - val psiManager = PsiManager.getInstance(project) - val applicationPackage = layoutXmlFileManager.androidModule?.applicationPackage - - return files.mapIndexed { index, syntheticFile -> - val fileName = AndroidConst.SYNTHETIC_FILENAME_PREFIX + syntheticFile.name + ".kt" - val virtualFile = LightVirtualFile(fileName, syntheticFile.contents) - val jetFile = psiManager.findFile(virtualFile) as KtFile - if (applicationPackage != null) { - jetFile.putUserData(AndroidConst.ANDROID_USER_PACKAGE, applicationPackage) - } - jetFile - } - } - - public companion object { - private val EXPLICIT_FLEXIBLE_PACKAGE = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.packageFqName.asString() - private val EXPLICIT_FLEXIBLE_CLASS_NAME = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.relativeClassName.asString() - - private val ANDROID_IMPORTS = listOf(Flexibility.FLEXIBLE_TYPE_CLASSIFIER.asSingleFqName().asString()) - - private val INVALID_WIDGET_TYPE_ANNOTATION = "InvalidWidgetType" - public val INVALID_WIDGET_TYPE_ANNOTATION_TYPE_PARAMETER: String = "type" - public val INVALID_WIDGET_TYPE_ANNOTATION_FQNAME: FqName = FqName("$EXPLICIT_FLEXIBLE_PACKAGE.$INVALID_WIDGET_TYPE_ANNOTATION") - - private val TOOLS_FILE = AndroidSyntheticFile.create("tools", - "package $EXPLICIT_FLEXIBLE_PACKAGE", - "class $EXPLICIT_FLEXIBLE_CLASS_NAME", - "annotation class $INVALID_WIDGET_TYPE_ANNOTATION(public val $INVALID_WIDGET_TYPE_ANNOTATION_TYPE_PARAMETER: String)") - } - -} \ No newline at end of file diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/androidResources.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/androidResources.kt index 3c51021daf9..86ae42b3bb4 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/androidResources.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/androidResources.kt @@ -16,7 +16,17 @@ 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) { val packageName: String = name @@ -28,61 +38,52 @@ class AndroidVariant(val name: String, val resDirectories: List) { } } -public class AndroidModule(val applicationPackage: String, val variants: List) { +class AndroidModule(val applicationPackage: String, val variants: List) { override fun equals(other: Any?) = other is AndroidModule && applicationPackage == other.applicationPackage override fun hashCode() = applicationPackage.hashCode() } -public abstract class AndroidResource(val id: String) { - public abstract val className: String - public open val supportClassName: String - get() = className +sealed class AndroidResource(val id: String, val sourceElement: PsiElement?) { + open fun sameClass(other: AndroidResource): Boolean = false - public abstract val mainProperties: List> - public open val mainPropertiesForSupportV4: List> = listOf() - public open val viewProperties: List> = listOf() - - public open fun sameClass(other: AndroidResource): Boolean = false -} - -public class AndroidWidget( - id: String, - override val className: String, - val invalidType: String? = null // When widget type is invalid, this value is the widget tag name -) : AndroidResource(id) { - private companion object { - val MAIN_PROPERTIES = listOf( - AndroidConst.ACTIVITY_FQNAME to "findViewById(0)", - AndroidConst.FRAGMENT_FQNAME to "getView().findViewById(0)") - - val MAIN_PROPERTIES_SUPPORT_V4 = listOf(AndroidConst.SUPPORT_FRAGMENT_FQNAME to "getView().findViewById(0)") - - val VIEW_PROPERTIES = listOf(AndroidConst.VIEW_FQNAME to "findViewById(0)") + class Widget( + id: String, + val xmlType: String, + sourceElement: PsiElement? + ) : AndroidResource(id, sourceElement) { + override fun sameClass(other: AndroidResource) = other is Widget } - override val mainProperties = MAIN_PROPERTIES - override val mainPropertiesForSupportV4 = MAIN_PROPERTIES_SUPPORT_V4 - override val viewProperties = VIEW_PROPERTIES - - override fun sameClass(other: AndroidResource) = other is AndroidWidget + class Fragment(id: String, sourceElement: PsiElement?) : AndroidResource(id, sourceElement) { + override fun sameClass(other: AndroidResource) = other is Fragment + } } -public class AndroidFragment(id: String) : AndroidResource(id) { - private companion object { - val MAIN_PROPERTIES = listOf( - AndroidConst.ACTIVITY_FQNAME to "getFragmentManager().findFragmentById(0)", - AndroidConst.FRAGMENT_FQNAME to "getFragmentManager().findFragmentById(0)") +fun cachedValue(project: Project, result: () -> CachedValueProvider.Result): CachedValue { + return CachedValuesManager.getManager(project).createCachedValue(result, false) +} - val MAIN_PROPERTIES_SUPPORT_V4 = listOf( - AndroidConst.SUPPORT_FRAGMENT_FQNAME to "getFragmentManager().findFragmentById(0)", - AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME to "getSupportFragmentManager().findFragmentById(0)") +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)) } - override val className = AndroidConst.FRAGMENT_FQNAME - override val supportClassName = AndroidConst.SUPPORT_FRAGMENT_FQNAME + for (packageName in AndroidConst.FQNAME_RESOLVE_PACKAGES) { + val classDescriptor = resolve("$packageName.$xmlType") + if (classDescriptor != null) { + return ResolvedWidget(this, classDescriptor) + } + } - override val mainProperties = MAIN_PROPERTIES - override val mainPropertiesForSupportV4 = MAIN_PROPERTIES_SUPPORT_V4 - - override fun sameClass(other: AndroidResource) = other is AndroidFragment + return ResolvedWidget(this, null) } \ No newline at end of file diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/syntheticDescriptorGeneration.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/syntheticDescriptorGeneration.kt new file mode 100644 index 00000000000..7681dc26bf3 --- /dev/null +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/syntheticDescriptorGeneration.kt @@ -0,0 +1,137 @@ +/* + * 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.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, resolvedWidget.errorType) +} + +internal fun genPropertyForFragment( + packageFragmentDescriptor: PackageFragmentDescriptor, + receiverType: KotlinType, + type: KotlinType, + fragment: AndroidResource.Fragment +): PropertyDescriptor { + val sourceElement = fragment.sourceElement?.let { XmlSourceElement(it) } ?: SourceElement.NO_SOURCE + return genProperty(fragment.id, receiverType, type, packageFragmentDescriptor, sourceElement, null) +} + +private fun genProperty( + id: String, + receiverType: KotlinType, + type: KotlinType, + containingDeclaration: DeclarationDescriptor, + sourceElement: SourceElement, + errorType: String? +): PropertyDescriptor { + 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 + } + + val flexibleType = DelegatingFlexibleType.create(type, type.makeNullable(), FlexibleTypeCapabilities.NONE) + property.setType( + flexibleType, + emptyList(), + null, + receiverType) + + val getter = PropertyGetterDescriptorImpl( + property, + Annotations.EMPTY, + Modality.FINAL, + Visibilities.PUBLIC, + false, + 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 isErrorType: Boolean + get() = errorType != null +} \ No newline at end of file