diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index 04892e33fb4..715db12a6f7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -131,38 +131,39 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { boolean isAnnotation = false; boolean isEnum = false; - if (myClass instanceof KtClass) { - KtClass ktClass = (KtClass) myClass; - if (ktClass.hasModifier(KtTokens.ABSTRACT_KEYWORD) || ktClass.isSealed()) { + ClassKind kind = descriptor.getKind(); + + if (kind == ClassKind.OBJECT) { + isStatic = isCompanionObject(descriptor); + isFinal = true; + } + else { + Modality modality = descriptor.getModality(); + + if (modality == Modality.ABSTRACT || modality == Modality.SEALED) { isAbstract = true; } - if (ktClass.isInterface()) { + + if (kind == ClassKind.INTERFACE) { isAbstract = true; isInterface = true; } - else if (descriptor.getKind() == ClassKind.ANNOTATION_CLASS) { + else if (kind == ClassKind.ANNOTATION_CLASS) { isAbstract = true; isInterface = true; isAnnotation = true; } - else if (ktClass.isEnum()) { + else if (kind == ClassKind.ENUM_CLASS) { isAbstract = hasAbstractMembers(descriptor); isEnum = true; } - if (isObject(descriptor)) { - isFinal = true; + if (modality != Modality.OPEN && !isAbstract) { + // Light-class mode: Do not make enum classes final since PsiClass corresponding to enum is expected to be inheritable from + isFinal = !(kind == ClassKind.ENUM_CLASS && !state.getClassBuilderMode().generateBodies); } - if (!ktClass.hasModifier(KtTokens.OPEN_KEYWORD) && !isAbstract) { - // Light-class mode: Do not make enum classes final since PsiClass corresponding to enum is expected to be inheritable from - isFinal = !(ktClass.isEnum() && !state.getClassBuilderMode().generateBodies); - } - isStatic = !ktClass.isInner(); - } - else { - isStatic = isCompanionObject(descriptor); - isFinal = true; + isStatic = !descriptor.isInner(); } int access = 0; 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 a38151a5c64..313a632c789 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 @@ -82,6 +82,7 @@ import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.load.kotlin.JvmVirtualFileFinderFactory @@ -170,6 +171,7 @@ class KotlinCoreEnvironment private constructor( AnalysisHandlerExtension.registerExtensionPoint(project) PackageFragmentProviderExtension.registerExtensionPoint(project) StorageComponentContainerContributor.registerExtensionPoint(project) + DeclarationAttributeAltererExtension.registerExtensionPoint(project) for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) { registrar.registerProjectComponents(project, configuration) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/extensions/DeclarationAttributeAltererExtension.kt b/compiler/frontend/src/org/jetbrains/kotlin/extensions/DeclarationAttributeAltererExtension.kt new file mode 100644 index 00000000000..3e20bca1026 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/extensions/DeclarationAttributeAltererExtension.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2016 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.extensions + +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.psi.KtModifierListOwner +import org.jetbrains.kotlin.resolve.BindingContext + +interface DeclarationAttributeAltererExtension { + companion object : ProjectExtensionDescriptor( + "org.jetbrains.kotlin.declarationAttributeAltererExtension", + DeclarationAttributeAltererExtension::class.java + ) + + /** + * Returns the new modality for the [declaration], or null if the [currentModality] is good enough. + */ + fun refineDeclarationModality( + modifierListOwner: KtModifierListOwner, + declaration: DeclarationDescriptor?, + containingDeclaration: DeclarationDescriptor?, + currentModality: Modality, + bindingContext: BindingContext + ): Modality? = null +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java index 1356a21af6c..9938684c193 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java @@ -780,7 +780,8 @@ public class DescriptorResolver { boolean hasBody = hasBody(property); Visibility visibility = resolveVisibilityFromModifiers(property, getDefaultVisibility(property, containingDeclaration)); Modality modality = containingDeclaration instanceof ClassDescriptor - ? resolveMemberModalityFromModifiers(property, getDefaultModality(containingDeclaration, visibility, hasBody)) + ? resolveMemberModalityFromModifiers(property, getDefaultModality(containingDeclaration, visibility, hasBody), + trace.getBindingContext(), containingDeclaration) : Modality.FINAL; final AnnotationSplitter.PropertyWrapper wrapper = new AnnotationSplitter.PropertyWrapper(property); @@ -923,7 +924,7 @@ public class DescriptorResolver { @NotNull KtProperty property, @NotNull PropertyDescriptor propertyDescriptor, @NotNull AnnotationSplitter annotationSplitter, - BindingTrace trace + @NotNull BindingTrace trace ) { KtPropertyAccessor setter = property.getSetter(); PropertySetterDescriptorImpl setterDescriptor = null; @@ -935,7 +936,8 @@ public class DescriptorResolver { setterDescriptor = new PropertySetterDescriptorImpl( propertyDescriptor, annotations, - resolveMemberModalityFromModifiers(setter, propertyDescriptor.getModality()), + resolveMemberModalityFromModifiers(setter, propertyDescriptor.getModality(), + trace.getBindingContext(), propertyDescriptor.getContainingDeclaration()), resolveVisibilityFromModifiers(setter, propertyDescriptor.getVisibility()), /* isDefault = */ false, setter.hasModifier(EXTERNAL_KEYWORD), property.hasModifier(KtTokens.INLINE_KEYWORD) || setter.hasModifier(KtTokens.INLINE_KEYWORD), @@ -1001,7 +1003,7 @@ public class DescriptorResolver { @NotNull KtProperty property, @NotNull PropertyDescriptor propertyDescriptor, @NotNull AnnotationSplitter annotationSplitter, - BindingTrace trace, + @NotNull BindingTrace trace, @Nullable KotlinType propertyTypeIfKnown ) { PropertyGetterDescriptorImpl getterDescriptor; @@ -1014,7 +1016,8 @@ public class DescriptorResolver { getterDescriptor = new PropertyGetterDescriptorImpl( propertyDescriptor, getterAnnotations, - resolveMemberModalityFromModifiers(getter, propertyDescriptor.getModality()), + resolveMemberModalityFromModifiers(getter, propertyDescriptor.getModality(), + trace.getBindingContext(), propertyDescriptor.getContainingDeclaration()), resolveVisibilityFromModifiers(getter, propertyDescriptor.getVisibility()), /* isDefault = */ false, getter.hasModifier(EXTERNAL_KEYWORD), property.hasModifier(KtTokens.INLINE_KEYWORD) || getter.hasModifier(KtTokens.INLINE_KEYWORD), @@ -1120,7 +1123,7 @@ public class DescriptorResolver { PropertyDescriptorImpl propertyDescriptor = PropertyDescriptorImpl.create( classDescriptor, propertyAnnotations, - resolveMemberModalityFromModifiers(parameter, Modality.FINAL), + resolveMemberModalityFromModifiers(parameter, Modality.FINAL, trace.getBindingContext(), classDescriptor), resolveVisibilityFromModifiers(parameter, getDefaultVisibility(parameter, classDescriptor)), isMutable, name, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt index 326e2df139e..7708ac0d853 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt @@ -168,7 +168,8 @@ class FunctionDescriptorResolver( val returnType = function.typeReference?.let { typeResolver.resolveType(headerScope, it, trace, true) } val visibility = resolveVisibilityFromModifiers(function, getDefaultVisibility(function, containingDescriptor)) - val modality = resolveMemberModalityFromModifiers(function, getDefaultModality(containingDescriptor, visibility, function.hasBody())) + val modality = resolveMemberModalityFromModifiers(function, getDefaultModality(containingDescriptor, visibility, function.hasBody()), + trace.bindingContext, containingDescriptor) functionDescriptor.initialize( receiverType, getDispatchReceiverParameterIfNeeded(containingDescriptor), diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java index 05f21ea6d8f..68716898525 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.config.LanguageVersionSettings; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1; +import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension; import org.jetbrains.kotlin.lexer.KtKeywordToken; import org.jetbrains.kotlin.lexer.KtModifierKeywordToken; import org.jetbrains.kotlin.lexer.KtTokens; @@ -102,15 +103,50 @@ public class ModifiersChecker { @NotNull public static Modality resolveMemberModalityFromModifiers( - @NotNull KtModifierListOwner modifierListOwner, - @NotNull Modality defaultModality + @Nullable KtModifierListOwner modifierListOwner, + @NotNull Modality defaultModality, + @NotNull BindingContext bindingContext, + @Nullable DeclarationDescriptor containingDescriptor ) { - return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality, /* allowSealed = */ false); + return resolveModalityFromModifiers(modifierListOwner, defaultModality, + bindingContext, containingDescriptor, /* allowSealed = */ false); } @NotNull public static Modality resolveModalityFromModifiers( - @Nullable KtModifierList modifierList, @NotNull Modality defaultModality, boolean allowSealed + @Nullable KtModifierListOwner modifierListOwner, + @NotNull Modality defaultModality, + @NotNull BindingContext bindingContext, + @Nullable DeclarationDescriptor containingDescriptor, + boolean allowSealed + ) { + KtModifierList modifierList = (modifierListOwner != null) ? modifierListOwner.getModifierList() : null; + Modality modality = resolveModalityFromModifiers(modifierList, defaultModality, allowSealed); + + if (modifierListOwner != null) { + Collection extensions = + DeclarationAttributeAltererExtension.Companion.getInstances(modifierListOwner.getProject()); + + DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, modifierListOwner); + for (DeclarationAttributeAltererExtension extension : extensions) { + Modality newModality = extension.refineDeclarationModality( + modifierListOwner, descriptor, containingDescriptor, modality, bindingContext); + + if (newModality != null) { + modality = newModality; + break; + } + } + } + + return modality; + } + + @NotNull + private static Modality resolveModalityFromModifiers( + @Nullable KtModifierList modifierList, + @NotNull Modality defaultModality, + boolean allowSealed ) { if (modifierList == null) return defaultModality; boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java index cc1913e06f5..65082acfdfb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java @@ -83,7 +83,7 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes private final ClassMemberDeclarationProvider declarationProvider; private final LazyClassTypeConstructor typeConstructor; - private final Modality modality; + private final NotNullLazyValue modality; private final Visibility visibility; private final ClassKind kind; private final boolean isInner; @@ -120,7 +120,7 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes ); this.c = c; - KtClassOrObject classOrObject = classLikeInfo.getCorrespondingClassOrObject(); + final KtClassOrObject classOrObject = classLikeInfo.getCorrespondingClassOrObject(); if (classOrObject != null) { this.c.getTrace().record(BindingContext.CLASS, classOrObject, this); } @@ -138,13 +138,26 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes this.isCompanionObject = classLikeInfo instanceof KtObjectInfo && ((KtObjectInfo) classLikeInfo).isCompanionObject(); - KtModifierList modifierList = classLikeInfo.getModifierList(); + final KtModifierList modifierList = classLikeInfo.getModifierList(); if (kind.isSingleton()) { - this.modality = Modality.FINAL; + this.modality = storageManager.createLazyValue(new Function0() { + @Override + public Modality invoke() { + return Modality.FINAL; + } + }); } else { - Modality defaultModality = kind == ClassKind.INTERFACE ? Modality.ABSTRACT : Modality.FINAL; - this.modality = resolveModalityFromModifiers(modifierList, defaultModality, /* allowSealed = */ true); + final Modality defaultModality = kind == ClassKind.INTERFACE ? Modality.ABSTRACT : Modality.FINAL; + this.modality = storageManager.createLazyValue(new Function0() { + @Override + public Modality invoke() { + return resolveModalityFromModifiers(classOrObject, defaultModality, + c.getTrace().getBindingContext(), + null, + /* allowSealed = */ true); + } + }); } boolean isLocal = classOrObject != null && KtPsiUtil.isLocal(classOrObject); @@ -473,7 +486,7 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes @NotNull @Override public Modality getModality() { - return modality; + return modality.invoke(); } @NotNull diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 5b5f9a81f8c..3bb8ce6112c 100755 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -20,6 +20,7 @@ import junit.framework.TestCase import org.intellij.lang.annotations.Language import org.jetbrains.kotlin.AbstractDataFlowValueRenderingTest import org.jetbrains.kotlin.addImport.AbstractAddImportTest +import org.jetbrains.kotlin.allopen.AbstractBytecodeListingTestForAllOpen import org.jetbrains.kotlin.android.* import org.jetbrains.kotlin.android.configure.AbstractConfigureProjectTest import org.jetbrains.kotlin.android.intentions.AbstractAndroidIntentionTest @@ -1158,6 +1159,12 @@ fun main(args: Array) { } } + testGroup("plugins/plugins-tests/tests", "plugins/allopen/testData") { + testClass() { + model("bytecodeListing", extension = "kt") + } + } + testGroup("plugins/android-extensions/android-extensions-idea/tests", "plugins/android-extensions/android-extensions-idea/testData") { testClass() { model("android/completion", recursive = false, extension = null) diff --git a/plugins/allopen/src/AllOpenDeclarationAttributeAltererExtension.kt b/plugins/allopen/src/AllOpenDeclarationAttributeAltererExtension.kt new file mode 100644 index 00000000000..fc91915c3ff --- /dev/null +++ b/plugins/allopen/src/AllOpenDeclarationAttributeAltererExtension.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2010-2016 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.allopen + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtModifierListOwner +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.TypeUtils + +class AllOpenDeclarationAttributeAltererExtension(val allOpenAnnotationFqNames: List) : DeclarationAttributeAltererExtension { + private companion object { + private val INHERITED_FQNAME = "java.lang.annotation.Inherited" + } + + override fun refineDeclarationModality( + modifierListOwner: KtModifierListOwner, + declaration: DeclarationDescriptor?, + containingDeclaration: DeclarationDescriptor?, + currentModality: Modality, + bindingContext: BindingContext + ): Modality? { + // We alter only 'final' modality + when (currentModality) { + Modality.OPEN, Modality.ABSTRACT, Modality.SEALED -> return null + else -> {} + } + + // Explicit final + if (modifierListOwner.hasModifier(KtTokens.FINAL_KEYWORD)) { + return null + } + + val descriptor = declaration ?: containingDeclaration ?: return null + if (descriptor.hasAllOpenAnnotation()) return Modality.OPEN + + return null + } + + private fun DeclarationDescriptor.hasAllOpenAnnotation(): Boolean { + if (annotations.any { it.isAllOpenAnnotation() }) return true + + if (this is ClassDescriptor) { + for (superType in TypeUtils.getAllSupertypes(defaultType)) { + val superTypeDescriptor = superType.constructor.declarationDescriptor as? ClassDescriptor ?: continue + if (superTypeDescriptor.annotations.any { it.isAllOpenAnnotation() }) return true + } + } + + return false + } + + private fun AnnotationDescriptor.isAllOpenAnnotation(allowMetaAnnotations: Boolean = true): Boolean { + val annotationType = type.constructor.declarationDescriptor ?: return false + if (annotationType.fqNameSafe.asString() in allOpenAnnotationFqNames) return true + + if (allowMetaAnnotations) { + for (metaAnnotation in annotationType.annotations) { + if (metaAnnotation.isAllOpenAnnotation(allowMetaAnnotations = false)) { + return true + } + } + } + + return false + } +} \ No newline at end of file diff --git a/plugins/allopen/src/AllOpenPlugin.kt b/plugins/allopen/src/AllOpenPlugin.kt index d5af5b3c891..688ec5e1bf4 100644 --- a/plugins/allopen/src/AllOpenPlugin.kt +++ b/plugins/allopen/src/AllOpenPlugin.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfigurationKey +import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension object AllOpenConfigurationKeys { val ANNOTATION: CompilerConfigurationKey> = @@ -53,6 +54,6 @@ class AllOpenComponentRegistrar : ComponentRegistrar { val annotations = configuration.get(AllOpenConfigurationKeys.ANNOTATION) ?: return if (annotations.isEmpty()) return - //TODO + DeclarationAttributeAltererExtension.registerExtension(project, AllOpenDeclarationAttributeAltererExtension(annotations)) } } \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/allOpenOnNotClasses.kt b/plugins/allopen/testData/bytecodeListing/allOpenOnNotClasses.kt new file mode 100644 index 00000000000..06f09020ab6 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/allOpenOnNotClasses.kt @@ -0,0 +1,19 @@ +@AllOpen +annotation class AllOpen + +@AllOpen +interface Intf + +@AllOpen +object Obj + +@AllOpen +enum class Enum + +class MyClass { + @AllOpen + fun method() {} + + @field:AllOpen @get:AllOpen @set:AllOpen + var prop: String = "" +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/allOpenOnNotClasses.txt b/plugins/allopen/testData/bytecodeListing/allOpenOnNotClasses.txt new file mode 100644 index 00000000000..8f32140193e --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/allOpenOnNotClasses.txt @@ -0,0 +1,35 @@ +@AllOpen +@java.lang.annotation.Retention +@kotlin.Metadata +public annotation class AllOpen + +@AllOpen +@kotlin.Metadata +public enum class Enum { + private synthetic final static field $VALUES: Enum[] + static method (): void + protected method (p0: java.lang.String, p1: int): void + public static method valueOf(p0: java.lang.String): Enum + public static method values(): Enum[] +} + +@AllOpen +@kotlin.Metadata +public interface Intf + +@kotlin.Metadata +public final class MyClass { + private @AllOpen @org.jetbrains.annotations.NotNull field prop: java.lang.String + public method (): void + public final @AllOpen @org.jetbrains.annotations.NotNull method getProp(): java.lang.String + public final @AllOpen method method(): void + public final @AllOpen method setProp(@org.jetbrains.annotations.NotNull p0: java.lang.String): void +} + +@AllOpen +@kotlin.Metadata +public final class Obj { + public final static field INSTANCE: Obj + static method (): void + private method (): void +} diff --git a/plugins/allopen/testData/bytecodeListing/alreadyOpen.kt b/plugins/allopen/testData/bytecodeListing/alreadyOpen.kt new file mode 100644 index 00000000000..cf33dd507f7 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/alreadyOpen.kt @@ -0,0 +1,16 @@ +annotation class AllOpen + +@AllOpen +open class Test1 + +@AllOpen +open class Test2 { + open fun method() {} + val prop: String = "" +} + +@AllOpen +class Test3 { + fun method() {} + open val prop: String = "" +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/alreadyOpen.txt b/plugins/allopen/testData/bytecodeListing/alreadyOpen.txt new file mode 100644 index 00000000000..77c8a9ed215 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/alreadyOpen.txt @@ -0,0 +1,27 @@ +@java.lang.annotation.Retention +@kotlin.Metadata +public annotation class AllOpen + +@AllOpen +@kotlin.Metadata +public class Test1 { + public method (): void +} + +@AllOpen +@kotlin.Metadata +public class Test2 { + private final @org.jetbrains.annotations.NotNull field prop: java.lang.String + public method (): void + public @org.jetbrains.annotations.NotNull method getProp(): java.lang.String + public method method(): void +} + +@AllOpen +@kotlin.Metadata +public class Test3 { + private final @org.jetbrains.annotations.NotNull field prop: java.lang.String + public method (): void + public @org.jetbrains.annotations.NotNull method getProp(): java.lang.String + public method method(): void +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/explicitFinal.kt b/plugins/allopen/testData/bytecodeListing/explicitFinal.kt new file mode 100644 index 00000000000..59f4e90b632 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/explicitFinal.kt @@ -0,0 +1,13 @@ +annotation class AllOpen + +@AllOpen +final class Test1 + +@AllOpen +class Test2 { + fun method1() {} + val prop1: String = "" + + final fun method2() {} + val prop2: String = "" +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/explicitFinal.txt b/plugins/allopen/testData/bytecodeListing/explicitFinal.txt new file mode 100644 index 00000000000..4d80cc83279 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/explicitFinal.txt @@ -0,0 +1,21 @@ +@java.lang.annotation.Retention +@kotlin.Metadata +public annotation class AllOpen + +@AllOpen +@kotlin.Metadata +public final class Test1 { + public method (): void +} + +@AllOpen +@kotlin.Metadata +public class Test2 { + private final @org.jetbrains.annotations.NotNull field prop1: java.lang.String + private final @org.jetbrains.annotations.NotNull field prop2: java.lang.String + public method (): void + public @org.jetbrains.annotations.NotNull method getProp1(): java.lang.String + public @org.jetbrains.annotations.NotNull method getProp2(): java.lang.String + public method method1(): void + public final method method2(): void +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/metaAnnotation.kt b/plugins/allopen/testData/bytecodeListing/metaAnnotation.kt new file mode 100644 index 00000000000..078b1c26757 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/metaAnnotation.kt @@ -0,0 +1,33 @@ +annotation class AllOpen + +@AllOpen +annotation class MyComponent + +@MyComponent // Double-transitive annotations is not supported +annotation class OtherComponent + +class TestWithoutAnnotations_ShouldBeFinal + +@AllOpen +class TestAllOpen_ShouldBeOpen + +@MyComponent +class TestMyComponent_ShouldBeOpen + +@OtherComponent +class TestOtherComponent_ShouldBeFinal + +@MyComponent +abstract class MyComponentBase + +class MyComponentImpl_ShouldBeOpen : MyComponentBase() { + fun method() {} +} + +final class MyComponentImpl2_ShouldBeFinal : MyComponentBase() { + fun method() {} +} + +class MyComponentImpl3_ShouldBeOpen : MyComponentBase() { + final fun method_ShouldBeFinal() {} +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/metaAnnotation.txt b/plugins/allopen/testData/bytecodeListing/metaAnnotation.txt new file mode 100644 index 00000000000..71d36becf07 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/metaAnnotation.txt @@ -0,0 +1,60 @@ +@java.lang.annotation.Retention +@kotlin.Metadata +public annotation class AllOpen + +@AllOpen +@java.lang.annotation.Retention +@kotlin.Metadata +public annotation class MyComponent + +@MyComponent +@kotlin.Metadata +public abstract class MyComponentBase { + public method (): void +} + +@kotlin.Metadata +public final class MyComponentImpl2_ShouldBeFinal { + public method (): void + public method method(): void +} + +@kotlin.Metadata +public class MyComponentImpl3_ShouldBeOpen { + public method (): void + public final method method_ShouldBeFinal(): void +} + +@kotlin.Metadata +public class MyComponentImpl_ShouldBeOpen { + public method (): void + public method method(): void +} + +@MyComponent +@java.lang.annotation.Retention +@kotlin.Metadata +public annotation class OtherComponent + +@AllOpen +@kotlin.Metadata +public class TestAllOpen_ShouldBeOpen { + public method (): void +} + +@MyComponent +@kotlin.Metadata +public class TestMyComponent_ShouldBeOpen { + public method (): void +} + +@OtherComponent +@kotlin.Metadata +public final class TestOtherComponent_ShouldBeFinal { + public method (): void +} + +@kotlin.Metadata +public final class TestWithoutAnnotations_ShouldBeFinal { + public method (): void +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/nestedInner.kt b/plugins/allopen/testData/bytecodeListing/nestedInner.kt new file mode 100644 index 00000000000..7d7c9a88dc9 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/nestedInner.kt @@ -0,0 +1,14 @@ +annotation class AllOpen + +@AllOpen +class Test { + fun testMethod() {} + + class Nested { + fun nestedMethod() {} + } + + inner class Inner { + fun innerMethod() {} + } +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/nestedInner.txt b/plugins/allopen/testData/bytecodeListing/nestedInner.txt new file mode 100644 index 00000000000..f0390263a84 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/nestedInner.txt @@ -0,0 +1,27 @@ +@java.lang.annotation.Retention +@kotlin.Metadata +public annotation class AllOpen + +@kotlin.Metadata +public final class Test$Inner { + synthetic final field this$0: Test + inner class Test$Inner + public method (p0: Test): void + public final method innerMethod(): void +} + +@kotlin.Metadata +public final class Test$Nested { + inner class Test$Nested + public method (): void + public final method nestedMethod(): void +} + +@AllOpen +@kotlin.Metadata +public class Test { + inner class Test$Inner + inner class Test$Nested + public method (): void + public method testMethod(): void +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/noAllOpen.kt b/plugins/allopen/testData/bytecodeListing/noAllOpen.kt new file mode 100644 index 00000000000..e6289d103bf --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/noAllOpen.kt @@ -0,0 +1,4 @@ +class Test { + val prop: String = "" + fun method() {} +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/noAllOpen.txt b/plugins/allopen/testData/bytecodeListing/noAllOpen.txt new file mode 100644 index 00000000000..3bbecec5672 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/noAllOpen.txt @@ -0,0 +1,7 @@ +@kotlin.Metadata +public final class Test { + private final @org.jetbrains.annotations.NotNull field prop: java.lang.String + public method (): void + public final @org.jetbrains.annotations.NotNull method getProp(): java.lang.String + public final method method(): void +} diff --git a/plugins/allopen/testData/bytecodeListing/sealed.kt b/plugins/allopen/testData/bytecodeListing/sealed.kt new file mode 100644 index 00000000000..aae22751093 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/sealed.kt @@ -0,0 +1,14 @@ +annotation class AllOpen + +@AllOpen +sealed class Sealed { + class C1 : Sealed() {} + class C2 : Sealed() {} +} + +sealed class Sealed2 { + @AllOpen + class C1 : Sealed2() {} + + class C2 : Sealed2() {} +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/sealed.txt b/plugins/allopen/testData/bytecodeListing/sealed.txt new file mode 100644 index 00000000000..22deef5b30f --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/sealed.txt @@ -0,0 +1,45 @@ +@java.lang.annotation.Retention +@kotlin.Metadata +public annotation class AllOpen + +@kotlin.Metadata +public class Sealed$C1 { + inner class Sealed$C1 + public method (): void +} + +@kotlin.Metadata +public class Sealed$C2 { + inner class Sealed$C2 + public method (): void +} + +@AllOpen +@kotlin.Metadata +public abstract class Sealed { + inner class Sealed$C1 + inner class Sealed$C2 + private method (): void + public synthetic method (p0: kotlin.jvm.internal.DefaultConstructorMarker): void +} + +@AllOpen +@kotlin.Metadata +public class Sealed2$C1 { + inner class Sealed2$C1 + public method (): void +} + +@kotlin.Metadata +public final class Sealed2$C2 { + inner class Sealed2$C2 + public method (): void +} + +@kotlin.Metadata +public abstract class Sealed2 { + inner class Sealed2$C1 + inner class Sealed2$C2 + private method (): void + public synthetic method (p0: kotlin.jvm.internal.DefaultConstructorMarker): void +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/severalAllOpen.kt b/plugins/allopen/testData/bytecodeListing/severalAllOpen.kt new file mode 100644 index 00000000000..57ad1c95cab --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/severalAllOpen.kt @@ -0,0 +1,9 @@ +annotation class AllOpen +annotation class AllOpen2 + +@AllOpen +@AllOpen2 +class Test { + val prop: String = "" + fun method() {} +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/severalAllOpen.txt b/plugins/allopen/testData/bytecodeListing/severalAllOpen.txt new file mode 100644 index 00000000000..93236a0c062 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/severalAllOpen.txt @@ -0,0 +1,17 @@ +@java.lang.annotation.Retention +@kotlin.Metadata +public annotation class AllOpen + +@java.lang.annotation.Retention +@kotlin.Metadata +public annotation class AllOpen2 + +@AllOpen +@AllOpen2 +@kotlin.Metadata +public class Test { + private final @org.jetbrains.annotations.NotNull field prop: java.lang.String + public method (): void + public @org.jetbrains.annotations.NotNull method getProp(): java.lang.String + public method method(): void +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/simple.kt b/plugins/allopen/testData/bytecodeListing/simple.kt new file mode 100644 index 00000000000..ee856696c53 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/simple.kt @@ -0,0 +1,7 @@ +annotation class AllOpen + +@AllOpen +class Test { + val prop: String = "" + fun method() {} +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/simple.txt b/plugins/allopen/testData/bytecodeListing/simple.txt new file mode 100644 index 00000000000..69b121b7fe2 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/simple.txt @@ -0,0 +1,12 @@ +@java.lang.annotation.Retention +@kotlin.Metadata +public annotation class AllOpen + +@AllOpen +@kotlin.Metadata +public class Test { + private final @org.jetbrains.annotations.NotNull field prop: java.lang.String + public method (): void + public @org.jetbrains.annotations.NotNull method getProp(): java.lang.String + public method method(): void +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/superClassAnnotation.kt b/plugins/allopen/testData/bytecodeListing/superClassAnnotation.kt new file mode 100644 index 00000000000..1a632a459ad --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/superClassAnnotation.kt @@ -0,0 +1,28 @@ +annotation class AllOpen + +@AllOpen +abstract class Base_ShouldBeOpen { + fun baseMethod() {} +} + +open class BaseImpl : Base_ShouldBeOpen() { + fun baseImplMethod_ShouldBeOpen() {} +} + +class BaseImpl2_ShouldBeOpen : BaseImpl() { + fun baseImpl2Method_ShouldBeOpen() {} + val baseImpl2Property_ShouldBeOpen = "" +} + +@AllOpen +interface Intf { + fun intfMethod() {} +} + +open class IntfImpl : Intf { + fun intfImplMethod_ShouldBeFinal() {} +} + +class IntfImpl2_ShouldBeFinalBecauseIntfIsAnInterface : IntfImpl() { + fun intfImpl2Method_ShouldBeFinal() {} +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/superClassAnnotation.txt b/plugins/allopen/testData/bytecodeListing/superClassAnnotation.txt new file mode 100644 index 00000000000..86363218132 --- /dev/null +++ b/plugins/allopen/testData/bytecodeListing/superClassAnnotation.txt @@ -0,0 +1,50 @@ +@java.lang.annotation.Retention +@kotlin.Metadata +public annotation class AllOpen + +@kotlin.Metadata +public class BaseImpl { + public method (): void + public method baseImplMethod_ShouldBeOpen(): void +} + +@kotlin.Metadata +public class BaseImpl2_ShouldBeOpen { + private final @org.jetbrains.annotations.NotNull field baseImpl2Property_ShouldBeOpen: java.lang.String + public method (): void + public method baseImpl2Method_ShouldBeOpen(): void + public @org.jetbrains.annotations.NotNull method getBaseImpl2Property_ShouldBeOpen(): java.lang.String +} + +@AllOpen +@kotlin.Metadata +public abstract class Base_ShouldBeOpen { + public method (): void + public method baseMethod(): void +} + +@kotlin.Metadata +public final class Intf$DefaultImpls { + inner class Intf$DefaultImpls + public static method intfMethod(p0: Intf): void +} + +@AllOpen +@kotlin.Metadata +public interface Intf { + inner class Intf$DefaultImpls + public abstract method intfMethod(): void +} + +@kotlin.Metadata +public class IntfImpl { + public method (): void + public final method intfImplMethod_ShouldBeFinal(): void + public method intfMethod(): void +} + +@kotlin.Metadata +public final class IntfImpl2_ShouldBeFinalBecauseIntfIsAnInterface { + public method (): void + public final method intfImpl2Method_ShouldBeFinal(): void +} \ No newline at end of file diff --git a/plugins/plugins-tests/plugins-tests.iml b/plugins/plugins-tests/plugins-tests.iml index b9fed3f8dd0..a7783d55b6e 100755 --- a/plugins/plugins-tests/plugins-tests.iml +++ b/plugins/plugins-tests/plugins-tests.iml @@ -37,5 +37,6 @@ + \ No newline at end of file diff --git a/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/AbstractBytecodeListingTestForAllOpen.kt b/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/AbstractBytecodeListingTestForAllOpen.kt new file mode 100644 index 00000000000..2cae74ffb82 --- /dev/null +++ b/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/AbstractBytecodeListingTestForAllOpen.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2010-2016 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.allopen + +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.codegen.AbstractBytecodeListingTest +import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension + +abstract class AbstractBytecodeListingTestForAllOpen : AbstractBytecodeListingTest() { + private companion object { + val ALLOPEN_ANNOTATIONS = listOf("AllOpen", "AllOpen2", "test.AllOpen") + } + + override fun setUpEnvironment(environment: KotlinCoreEnvironment) { + DeclarationAttributeAltererExtension.registerExtension( + environment.project, AllOpenDeclarationAttributeAltererExtension(ALLOPEN_ANNOTATIONS)) + } +} \ No newline at end of file diff --git a/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/BytecodeListingTestForAllOpenGenerated.java b/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/BytecodeListingTestForAllOpenGenerated.java new file mode 100644 index 00000000000..ab84d923a19 --- /dev/null +++ b/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/BytecodeListingTestForAllOpenGenerated.java @@ -0,0 +1,97 @@ +/* + * Copyright 2010-2016 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.allopen; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/allopen/testData/bytecodeListing") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class BytecodeListingTestForAllOpenGenerated extends AbstractBytecodeListingTestForAllOpen { + public void testAllFilesPresentInBytecodeListing() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/allopen/testData/bytecodeListing"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("allOpenOnNotClasses.kt") + public void testAllOpenOnNotClasses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/allOpenOnNotClasses.kt"); + doTest(fileName); + } + + @TestMetadata("alreadyOpen.kt") + public void testAlreadyOpen() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/alreadyOpen.kt"); + doTest(fileName); + } + + @TestMetadata("explicitFinal.kt") + public void testExplicitFinal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/explicitFinal.kt"); + doTest(fileName); + } + + @TestMetadata("metaAnnotation.kt") + public void testMetaAnnotation() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/metaAnnotation.kt"); + doTest(fileName); + } + + @TestMetadata("nestedInner.kt") + public void testNestedInner() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/nestedInner.kt"); + doTest(fileName); + } + + @TestMetadata("noAllOpen.kt") + public void testNoAllOpen() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/noAllOpen.kt"); + doTest(fileName); + } + + @TestMetadata("sealed.kt") + public void testSealed() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/sealed.kt"); + doTest(fileName); + } + + @TestMetadata("severalAllOpen.kt") + public void testSeveralAllOpen() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/severalAllOpen.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/simple.kt"); + doTest(fileName); + } + + @TestMetadata("superClassAnnotation.kt") + public void testSuperClassAnnotation() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/superClassAnnotation.kt"); + doTest(fileName); + } +}