diff --git a/compiler/tests-common/org/jetbrains/kotlin/codegen/CodegenTestCase.java b/compiler/tests-common/org/jetbrains/kotlin/codegen/CodegenTestCase.java index d1295994869..84b23bbb3ea 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/codegen/CodegenTestCase.java +++ b/compiler/tests-common/org/jetbrains/kotlin/codegen/CodegenTestCase.java @@ -321,10 +321,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase { @NotNull protected String relativePath(@NotNull File file) { - String stringToCut = codegenTestBasePath(); - String systemIndependentPath = file.getPath().replace(File.separatorChar, '/'); - assert systemIndependentPath.startsWith(stringToCut) : "File path is not absolute: " + file; - return systemIndependentPath.substring(stringToCut.length()); + return FilesKt.toRelativeString(file.getAbsoluteFile(), new File(codegenTestBasePath()).getAbsoluteFile()); } @NotNull diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidContainerType.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidContainerType.kt index 9fd233e82ee..ca38db6e375 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidContainerType.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidContainerType.kt @@ -16,11 +16,10 @@ package org.jetbrains.kotlin.android.synthetic.codegen +import kotlinx.android.extensions.LayoutContainer import org.jetbrains.kotlin.android.synthetic.AndroidConst import org.jetbrains.kotlin.descriptors.ClassifierDescriptor -import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor enum class AndroidContainerType(className: String, val doesSupportCache: Boolean = false, val isFragment: Boolean = false) { ACTIVITY(AndroidConst.ACTIVITY_FQNAME, doesSupportCache = true), @@ -29,11 +28,14 @@ enum class AndroidContainerType(className: String, val doesSupportCache: Boolean SUPPORT_FRAGMENT_ACTIVITY(AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME, doesSupportCache = true), SUPPORT_FRAGMENT(AndroidConst.SUPPORT_FRAGMENT_FQNAME, doesSupportCache = true, isFragment = true), VIEW(AndroidConst.VIEW_FQNAME, doesSupportCache = true), + LAYOUT_CONTAINER(LayoutContainer::class.java.canonicalName, doesSupportCache = true), UNKNOWN(""); val internalClassName: String = className.replace('.', '/') companion object { + private val LAYOUT_CONTAINER_FQNAME = LayoutContainer::class.java.canonicalName + fun get(descriptor: ClassifierDescriptor): AndroidContainerType { fun getClassTypeInternal(name: String): AndroidContainerType? = when (name) { AndroidConst.ACTIVITY_FQNAME -> AndroidContainerType.ACTIVITY @@ -42,17 +44,11 @@ enum class AndroidContainerType(className: String, val doesSupportCache: Boolean AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME -> AndroidContainerType.SUPPORT_FRAGMENT_ACTIVITY AndroidConst.SUPPORT_FRAGMENT_FQNAME -> AndroidContainerType.SUPPORT_FRAGMENT AndroidConst.VIEW_FQNAME -> AndroidContainerType.VIEW + LAYOUT_CONTAINER_FQNAME -> AndroidContainerType.LAYOUT_CONTAINER 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 - } + getClassTypeInternal(DescriptorUtils.getFqName(descriptor).asString())?.let { return it } for (supertype in descriptor.typeConstructor.supertypes) { val declarationDescriptor = supertype.constructor.declarationDescriptor diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt index 68a84f2db7d..2071ac25ffe 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt @@ -93,8 +93,7 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { return StackValue.functionCall(Type.VOID_TYPE) {} } - val androidClassType = AndroidContainerType.get(container) - if (androidClassType == AndroidContainerType.UNKNOWN) return null + if (containerOptions.classType == AndroidContainerType.UNKNOWN) return null return StackValue.functionCall(Type.VOID_TYPE) { val bytecodeClassName = c.typeMapper.mapType(container).internalName @@ -248,8 +247,17 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { loadId() iv.invokevirtual(containerOptions.classType.internalClassName, "findViewById", "(I)Landroid/view/View;", false) } - AndroidContainerType.FRAGMENT, AndroidContainerType.SUPPORT_FRAGMENT -> { - iv.invokevirtual(containerOptions.classType.internalClassName, "getView", "()Landroid/view/View;", false) + AndroidContainerType.FRAGMENT, AndroidContainerType.SUPPORT_FRAGMENT, AndroidContainerType.LAYOUT_CONTAINER -> { + val methodName: String + val targetClassName: String + if (containerOptions.classType == AndroidContainerType.LAYOUT_CONTAINER) { + methodName = "getEntityView" + targetClassName = classType.internalName + } else { + methodName = "getView" + targetClassName = containerOptions.classType.internalClassName + } + iv.invokevirtual(targetClassName, methodName, "()Landroid/view/View;", false) iv.dup() val lgetViewNotNull = Label() iv.ifnonnull(lgetViewNotNull) diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/ResourcePropertyStackValue.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/ResourcePropertyStackValue.kt index 4e1ef01b65f..8a2991408de 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/ResourcePropertyStackValue.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/ResourcePropertyStackValue.kt @@ -38,6 +38,10 @@ class ResourcePropertyStackValue( ) : StackValue(typeMapper.mapType(resource.returnType!!)) { private val androidClassType get() = containerOptions.classType + init { + assert(containerOptions.classType != AndroidContainerType.UNKNOWN) + } + override fun putSelector(type: Type, v: InstructionAdapter) { val returnTypeString = typeMapper.mapType(resource.type.lowerIfFlexible()).className if (AndroidConst.FRAGMENT_FQNAME == returnTypeString || AndroidConst.SUPPORT_FRAGMENT_FQNAME == returnTypeString) { @@ -69,6 +73,12 @@ class ResourcePropertyStackValue( getResourceId(v) v.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false) } + AndroidContainerType.LAYOUT_CONTAINER -> { + receiver.put(Type.getType("L${androidClassType.internalClassName};"), v) + v.invokevirtual(androidClassType.internalClassName, "getEntityView", "()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 } } diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/ContainerOptionsProxy.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/ContainerOptionsProxy.kt index 3fa857b4efe..7ebb616c8b4 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/ContainerOptionsProxy.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/ContainerOptionsProxy.kt @@ -22,6 +22,7 @@ import kotlinx.android.extensions.CacheImplementation.* import kotlinx.android.extensions.ContainerOptions import org.jetbrains.kotlin.android.synthetic.codegen.AndroidContainerType import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.constants.EnumValue @@ -35,6 +36,10 @@ class ContainerOptionsProxy(val classType: AndroidContainerType, val cache: Cach private val DEFAULT_CACHE_IMPL = HASH_MAP fun get(container: ClassDescriptor): ContainerOptionsProxy { + if (container.kind != ClassKind.CLASS) { + return ContainerOptionsProxy(AndroidContainerType.UNKNOWN, NO_CACHE) + } + val classType = AndroidContainerType.get(container) val anno = container.annotations.findAnnotation(CONTAINER_OPTIONS_FQNAME) @@ -42,7 +47,9 @@ class ContainerOptionsProxy(val classType: AndroidContainerType, val cache: Cach if (anno == null) { // Java classes (and Kotlin classes from other modules) does not support cache by default val supportsCache = container.source is KotlinSourceElement && classType.doesSupportCache - return ContainerOptionsProxy(classType, if (supportsCache) DEFAULT_CACHE_IMPL else NO_CACHE) + return ContainerOptionsProxy( + classType, + if (supportsCache) DEFAULT_CACHE_IMPL else NO_CACHE) } val cache = anno.getEnumValue(CACHE_NAME, HASH_MAP) { valueOf(it) } diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/LazySyntheticElementResolveContext.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/LazySyntheticElementResolveContext.kt index 4ffd8b7e776..2d36883dc67 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/LazySyntheticElementResolveContext.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/LazySyntheticElementResolveContext.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.android.synthetic.descriptors +import kotlinx.android.extensions.LayoutContainer import org.jetbrains.kotlin.android.synthetic.AndroidConst import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies @@ -43,56 +44,61 @@ class LazySyntheticElementResolveContext(private val module: ModuleDescriptor, s val dialogDescriptor = find(AndroidConst.DIALOG_FQNAME) ?: return SyntheticElementResolveContext.ERROR_CONTEXT val supportActivityDescriptor = find(AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME) val supportFragmentDescriptor = find(AndroidConst.SUPPORT_FRAGMENT_FQNAME) + val layoutContainerDescriptor = find(LayoutContainer::class.java.canonicalName) return SyntheticElementResolveContext( - viewDescriptor.defaultType, - activityDescriptor.defaultType, - fragmentDescriptor?.defaultType, - dialogDescriptor.defaultType, - supportActivityDescriptor?.defaultType, - supportFragmentDescriptor?.defaultType) + view = viewDescriptor.defaultType, + activity = activityDescriptor.defaultType, + fragment = fragmentDescriptor?.defaultType, + dialog = dialogDescriptor.defaultType, + supportActivity = supportActivityDescriptor?.defaultType, + supportFragment = supportFragmentDescriptor?.defaultType, + layoutContainer = layoutContainerDescriptor?.defaultType) } } internal class SyntheticElementResolveContext( - val viewType: SimpleType, - val activityType: SimpleType, - val fragmentType: SimpleType?, - val dialogType: SimpleType, - val supportActivityType: SimpleType?, - val supportFragmentType: SimpleType?) { + val view: SimpleType, + val activity: SimpleType, + val fragment: SimpleType?, + val dialog: SimpleType, + val supportActivity: SimpleType?, + val supportFragment: SimpleType?, + val layoutContainer: SimpleType? +) { companion object { private fun errorType() = ErrorUtils.createErrorType("") - val ERROR_CONTEXT = SyntheticElementResolveContext(errorType(), errorType(), null, errorType(), null, null) + val ERROR_CONTEXT = SyntheticElementResolveContext(errorType(), errorType(), null, errorType(), null, null, null) } private val widgetReceivers by lazy { val receivers = ArrayList(4) - receivers += WidgetReceiver(activityType, mayHaveCache = true) - receivers += WidgetReceiver(dialogType, mayHaveCache = false) - fragmentType?.let { receivers += WidgetReceiver(it, mayHaveCache = true) } - supportFragmentType?.let { receivers += WidgetReceiver(it, mayHaveCache = true) } + receivers += WidgetReceiver(activity, mayHaveCache = true) + receivers += WidgetReceiver(dialog, mayHaveCache = false) + fragment?.let { receivers += WidgetReceiver(it, mayHaveCache = true) } + supportFragment?.let { receivers += WidgetReceiver(it, mayHaveCache = true) } + layoutContainer?.let { receivers += WidgetReceiver(it, mayHaveCache = true) } receivers } val fragmentTypes: List> by lazy { - if (fragmentType == null) { + if (fragment == 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 += Pair(activity, fragment) + types += Pair(fragment, fragment) + if (supportActivity != null && supportFragment != null) { + types += Pair(supportFragment, supportFragment) + types += Pair(supportActivity, supportFragment) } types } } fun getWidgetReceivers(forView: Boolean): List { - if (forView) return listOf(WidgetReceiver(viewType, mayHaveCache = false)) + if (forView) return listOf(WidgetReceiver(view, mayHaveCache = true)) return widgetReceivers } diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt index 2ef39ed43d5..5d2c90ec080 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt @@ -79,7 +79,7 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider // Package with clearFindViewByIdCache() AndroidConst.SYNTHETIC_SUBPACKAGES.last().let { s -> val packageDescriptor = PredefinedPackageFragmentDescriptor(s, module, storageManager, packagesToLookupInCompletion) { descriptor -> - lazyContext().getWidgetReceivers(false) + (lazyContext().getWidgetReceivers(false) + lazyContext().getWidgetReceivers(true)) .filter { it.mayHaveCache } .map { genClearCacheFunction(descriptor, it.type) } } diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/syntheticDescriptorGeneration.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/syntheticDescriptorGeneration.kt index 7ddee6a8480..d0b62b426e1 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/syntheticDescriptorGeneration.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/syntheticDescriptorGeneration.kt @@ -69,7 +69,7 @@ internal fun genPropertyForWidget( KotlinTypeFactory.simpleNotNullType( Annotations.EMPTY, classDescriptor, defaultType.constructor.parameters.map(::StarProjectionImpl)) } - } ?: context.viewType + } ?: context.view return genProperty(resolvedWidget.widget, receiverType, type, packageFragmentDescriptor, sourceEl, resolvedWidget.errorType) } diff --git a/plugins/android-extensions/android-extensions-compiler/testData/codegen/android/androidEntity/0.kt b/plugins/android-extensions/android-extensions-compiler/testData/codegen/android/androidEntity/0.kt new file mode 100644 index 00000000000..859d766f9b2 --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/codegen/android/androidEntity/0.kt @@ -0,0 +1,36 @@ +package test + +import android.app.Activity +import android.view.View +import android.widget.* +import kotlinx.android.synthetic.main.layout.* +import kotlinx.android.extensions.* + +class R { + class id { + companion object { + const val login = 5 + } + } +} + +class MyEntity(override val containerView: View) : LayoutContainer + +class MyActivity(): Activity() { + val loginItem = Button(this) + + val entity = MyEntity(object : FrameLayout(this) { + override fun findViewById(id: Int) : View? = when(id) { + R.id.login -> loginItem + else -> null + } + }) + + public fun box(): String { + return if (entity.login.toString() == "Button") "OK" else "" + } +} + +fun box(): String { + return MyActivity().box() +} diff --git a/plugins/android-extensions/android-extensions-compiler/testData/codegen/android/androidEntity/1.kt b/plugins/android-extensions/android-extensions-compiler/testData/codegen/android/androidEntity/1.kt new file mode 100644 index 00000000000..6f97ca9dc98 --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/codegen/android/androidEntity/1.kt @@ -0,0 +1,30 @@ +package test + +import android.app.Activity +import android.view.View +import android.widget.* +import kotlinx.android.synthetic.main.layout.* +import kotlinx.android.extensions.* + +class R { + class id { + companion object { + const val login = 5 + } + } +} + +class MyEntity(override val containerView: View) : LayoutContainer + +class MyActivity(): Activity() { + val loginItem = Button(this) + val entity = MyEntity(loginItem) + + init { + entity.login + } +} + +fun box(): String { + return "OK" +} diff --git a/plugins/android-extensions/android-extensions-compiler/testData/codegen/android/androidEntity/AndroidManifest.xml b/plugins/android-extensions/android-extensions-compiler/testData/codegen/android/androidEntity/AndroidManifest.xml new file mode 100644 index 00000000000..00856b86107 --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/codegen/android/androidEntity/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-compiler/testData/codegen/android/androidEntity/layout/layout.xml b/plugins/android-extensions/android-extensions-compiler/testData/codegen/android/androidEntity/layout/layout.xml new file mode 100644 index 00000000000..3e1b7254a52 --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/codegen/android/androidEntity/layout/layout.xml @@ -0,0 +1,10 @@ + + +