| /* |
| * Copyright 2010-2017 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.descriptors; |
| |
| import kotlin.collections.SetsKt; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor; |
| import org.jetbrains.kotlin.resolve.DescriptorUtils; |
| import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; |
| import org.jetbrains.kotlin.resolve.scopes.receivers.SuperCallReceiverValue; |
| import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver; |
| import org.jetbrains.kotlin.types.DynamicTypesKt; |
| import org.jetbrains.kotlin.types.KotlinType; |
| import org.jetbrains.kotlin.util.ModuleVisibilityHelper; |
| import org.jetbrains.kotlin.utils.CollectionsKt; |
| |
| import java.util.*; |
| |
| public class Visibilities { |
| @NotNull |
| public static final Visibility PRIVATE = new Visibility("private", false) { |
| @Override |
| public boolean mustCheckInImports() { |
| return true; |
| } |
| |
| private boolean hasContainingSourceFile(@NotNull DeclarationDescriptor descriptor) { |
| return DescriptorUtils.getContainingSourceFile(descriptor) != SourceFile.NO_SOURCE_FILE; |
| } |
| |
| @Override |
| public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { |
| if (DescriptorUtils.isTopLevelDeclaration(what) && hasContainingSourceFile(from)) { |
| return inSameFile(what, from); |
| } |
| |
| if (what instanceof ConstructorDescriptor) { |
| ClassifierDescriptorWithTypeParameters classDescriptor = ((ConstructorDescriptor) what).getContainingDeclaration(); |
| if (DescriptorUtils.isSealedClass(classDescriptor) |
| && DescriptorUtils.isTopLevelDeclaration(classDescriptor) |
| && from instanceof ConstructorDescriptor |
| && DescriptorUtils.isTopLevelDeclaration(from.getContainingDeclaration()) |
| && inSameFile(what, from)) { |
| return true; |
| } |
| } |
| |
| DeclarationDescriptor parent = what; |
| while (parent != null) { |
| parent = parent.getContainingDeclaration(); |
| if ((parent instanceof ClassDescriptor && !DescriptorUtils.isCompanionObject(parent)) || |
| parent instanceof PackageFragmentDescriptor) { |
| break; |
| } |
| } |
| if (parent == null) { |
| return false; |
| } |
| DeclarationDescriptor fromParent = from; |
| while (fromParent != null) { |
| if (parent == fromParent) { |
| return true; |
| } |
| if (fromParent instanceof PackageFragmentDescriptor) { |
| return parent instanceof PackageFragmentDescriptor |
| && ((PackageFragmentDescriptor) parent).getFqName().equals(((PackageFragmentDescriptor) fromParent).getFqName()) |
| && DescriptorUtils.areInSameModule(fromParent, parent); |
| } |
| fromParent = fromParent.getContainingDeclaration(); |
| } |
| return false; |
| } |
| }; |
| |
| /** |
| * This visibility is needed for the next case: |
| * class A<in T>(t: T) { |
| * private val t: T = t // visibility for t is PRIVATE_TO_THIS |
| * |
| * fun test() { |
| * val x: T = t // correct |
| * val y: T = this.t // also correct |
| * } |
| * fun foo(a: A<String>) { |
| * val x: String = a.t // incorrect, because a.t can be Any |
| * } |
| * } |
| */ |
| @NotNull |
| public static final Visibility PRIVATE_TO_THIS = new Visibility("private_to_this", false) { |
| @Override |
| public boolean isVisible(@Nullable ReceiverValue thisObject, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { |
| if (PRIVATE.isVisible(thisObject, what, from)) { |
| // See Visibility.isVisible contract |
| if (thisObject == ALWAYS_SUITABLE_RECEIVER) return true; |
| if (thisObject == IRRELEVANT_RECEIVER) return false; |
| |
| DeclarationDescriptor classDescriptor = DescriptorUtils.getParentOfType(what, ClassDescriptor.class); |
| |
| if (classDescriptor != null && thisObject instanceof ThisClassReceiver) { |
| return ((ThisClassReceiver) thisObject).getClassDescriptor().getOriginal().equals(classDescriptor.getOriginal()); |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean mustCheckInImports() { |
| return true; |
| } |
| |
| @NotNull |
| @Override |
| public String getDisplayName() { |
| return "private/*private to this*/"; |
| } |
| }; |
| |
| @NotNull |
| public static final Visibility PROTECTED = new Visibility("protected", true) { |
| @Override |
| public boolean mustCheckInImports() { |
| return false; |
| } |
| |
| @Override |
| public boolean isVisible( |
| @Nullable ReceiverValue receiver, |
| @NotNull DeclarationDescriptorWithVisibility what, |
| @NotNull DeclarationDescriptor from |
| ) { |
| ClassDescriptor givenDescriptorContainingClass = DescriptorUtils.getParentOfType(what, ClassDescriptor.class); |
| ClassDescriptor fromClass = DescriptorUtils.getParentOfType(from, ClassDescriptor.class, false); |
| if (fromClass == null) return false; |
| |
| if (givenDescriptorContainingClass != null && DescriptorUtils.isCompanionObject(givenDescriptorContainingClass)) { |
| // Access to protected members inside companion is allowed to all subclasses |
| // Receiver type does not matter because objects are final |
| // NB: protected fake overrides in companion from super class should also be allowed |
| ClassDescriptor companionOwner = DescriptorUtils.getParentOfType(givenDescriptorContainingClass, ClassDescriptor.class); |
| if (companionOwner != null && DescriptorUtils.isSubclass(fromClass, companionOwner)) return true; |
| } |
| |
| // The rest part of method checks visibility similarly to Java does for protected (see JLS p.6.6.2) |
| |
| // Protected fake overrides can have only one protected overridden (as protected is not allowed for interface members) |
| DeclarationDescriptorWithVisibility whatDeclaration = DescriptorUtils.unwrapFakeOverrideToAnyDeclaration(what); |
| |
| ClassDescriptor classDescriptor = DescriptorUtils.getParentOfType(whatDeclaration, ClassDescriptor.class); |
| if (classDescriptor == null) return false; |
| |
| if (DescriptorUtils.isSubclass(fromClass, classDescriptor) |
| && doesReceiverFitForProtectedVisibility(receiver, whatDeclaration, fromClass)) { |
| return true; |
| } |
| |
| return isVisible(receiver, what, fromClass.getContainingDeclaration()); |
| } |
| |
| private boolean doesReceiverFitForProtectedVisibility( |
| @Nullable ReceiverValue receiver, |
| @NotNull DeclarationDescriptorWithVisibility whatDeclaration, |
| @NotNull ClassDescriptor fromClass |
| ) { |
| //noinspection deprecation |
| if (receiver == FALSE_IF_PROTECTED) return false; |
| |
| // Do not check receiver for non-callable declarations |
| if (!(whatDeclaration instanceof CallableMemberDescriptor)) return true; |
| // Constructor accessibility check is performed manually |
| if (whatDeclaration instanceof ConstructorDescriptor) return true; |
| |
| // See Visibility.isVisible contract |
| if (receiver == ALWAYS_SUITABLE_RECEIVER) return true; |
| if (receiver == IRRELEVANT_RECEIVER || receiver == null) return false; |
| |
| KotlinType actualReceiverType = receiver instanceof SuperCallReceiverValue |
| ? ((SuperCallReceiverValue) receiver).getThisType() |
| : receiver.getType(); |
| |
| return DescriptorUtils.isSubtypeOfClass(actualReceiverType, fromClass) || DynamicTypesKt.isDynamic(actualReceiverType); |
| } |
| }; |
| |
| @NotNull |
| public static final Visibility INTERNAL = new Visibility("internal", false) { |
| @Override |
| public boolean mustCheckInImports() { |
| return true; |
| } |
| |
| @Override |
| public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { |
| ModuleDescriptor whatModule = DescriptorUtils.getContainingModule(what); |
| ModuleDescriptor fromModule = DescriptorUtils.getContainingModule(from); |
| |
| // Can't invert this condition because CLI compiler analyzes sources as like all in the one module |
| // and for modules with circular dependency (chunk) JPS provides sources of all modules, |
| // so we can't be sure that references to an internal member are correct. |
| if (!fromModule.shouldSeeInternalsOf(whatModule)) return false; |
| |
| |
| return MODULE_VISIBILITY_HELPER.isInFriendModule(what, from); |
| } |
| }; |
| |
| @NotNull |
| public static final Visibility PUBLIC = new Visibility("public", true) { |
| @Override |
| public boolean mustCheckInImports() { |
| return false; |
| } |
| |
| @Override |
| public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { |
| return true; |
| } |
| }; |
| |
| @NotNull |
| public static final Visibility LOCAL = new Visibility("local", false) { |
| @Override |
| public boolean mustCheckInImports() { |
| throw new IllegalStateException("This method shouldn't be invoked for LOCAL visibility"); |
| } |
| |
| @Override |
| public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { |
| throw new IllegalStateException("This method shouldn't be invoked for LOCAL visibility"); |
| } |
| }; |
| |
| @NotNull |
| public static final Visibility INHERITED = new Visibility("inherited", false) { |
| @Override |
| public boolean mustCheckInImports() { |
| throw new IllegalStateException("This method shouldn't be invoked for INHERITED visibility"); |
| } |
| |
| @Override |
| public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { |
| throw new IllegalStateException("Visibility is unknown yet"); //This method shouldn't be invoked for INHERITED visibility |
| } |
| }; |
| |
| /* Visibility for fake override invisible members (they are created for better error reporting) */ |
| @NotNull |
| public static final Visibility INVISIBLE_FAKE = new Visibility("invisible_fake", false) { |
| @Override |
| public boolean mustCheckInImports() { |
| throw new IllegalStateException("This method shouldn't be invoked for INVISIBLE_FAKE visibility"); |
| } |
| |
| @Override |
| public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { |
| return false; |
| } |
| }; |
| |
| // Currently used as default visibility of FunctionDescriptor |
| // It's needed to prevent NPE when requesting non-nullable visibility of descriptor before `initialize` has been called |
| @NotNull |
| public static final Visibility UNKNOWN = new Visibility("unknown", false) { |
| @Override |
| public boolean mustCheckInImports() { |
| throw new IllegalStateException("This method shouldn't be invoked for UNKNOWN visibility"); |
| } |
| |
| @Override |
| public boolean isVisible( |
| @Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from |
| ) { |
| return false; |
| } |
| }; |
| |
| public static final Set<Visibility> INVISIBLE_FROM_OTHER_MODULES = |
| Collections.unmodifiableSet(SetsKt.setOf(PRIVATE, PRIVATE_TO_THIS, INTERNAL, LOCAL)); |
| |
| private Visibilities() { |
| } |
| |
| public static boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { |
| return findInvisibleMember(receiver, what, from) == null; |
| } |
| |
| /** |
| * @see Visibility.isVisible contract |
| */ |
| public static boolean isVisibleIgnoringReceiver(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { |
| return findInvisibleMember(ALWAYS_SUITABLE_RECEIVER, what, from) == null; |
| } |
| |
| /** |
| * @see Visibility.isVisible contract |
| * @see Visibilities.RECEIVER_DOES_NOT_EXIST |
| */ |
| public static boolean isVisibleWithAnyReceiver(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { |
| return findInvisibleMember(IRRELEVANT_RECEIVER, what, from) == null; |
| } |
| |
| // Note that this method returns false if `from` declaration is `init` initializer |
| // because initializer does not have source element |
| public static boolean inSameFile(@NotNull DeclarationDescriptor what, @NotNull DeclarationDescriptor from) { |
| SourceFile fromContainingFile = DescriptorUtils.getContainingSourceFile(from); |
| if (fromContainingFile != SourceFile.NO_SOURCE_FILE) { |
| return fromContainingFile.equals(DescriptorUtils.getContainingSourceFile(what)); |
| } |
| return false; |
| } |
| |
| @Nullable |
| public static DeclarationDescriptorWithVisibility findInvisibleMember( |
| @Nullable ReceiverValue receiver, |
| @NotNull DeclarationDescriptorWithVisibility what, |
| @NotNull DeclarationDescriptor from |
| ) { |
| DeclarationDescriptorWithVisibility parent = (DeclarationDescriptorWithVisibility) what.getOriginal(); |
| while (parent != null && parent.getVisibility() != LOCAL) { |
| if (!parent.getVisibility().isVisible(receiver, parent, from)) { |
| return parent; |
| } |
| parent = DescriptorUtils.getParentOfType(parent, DeclarationDescriptorWithVisibility.class); |
| } |
| |
| if (what instanceof TypeAliasConstructorDescriptor) { |
| DeclarationDescriptorWithVisibility invisibleUnderlying = |
| findInvisibleMember(receiver, ((TypeAliasConstructorDescriptor) what).getUnderlyingConstructorDescriptor(), from); |
| if (invisibleUnderlying != null) return invisibleUnderlying; |
| } |
| |
| return null; |
| } |
| |
| private static final Map<Visibility, Integer> ORDERED_VISIBILITIES; |
| |
| static { |
| Map<Visibility, Integer> visibilities = CollectionsKt.newHashMapWithExpectedSize(4); |
| visibilities.put(PRIVATE_TO_THIS, 0); |
| visibilities.put(PRIVATE, 0); |
| visibilities.put(INTERNAL, 1); |
| visibilities.put(PROTECTED, 1); |
| visibilities.put(PUBLIC, 2); |
| ORDERED_VISIBILITIES = Collections.unmodifiableMap(visibilities); |
| } |
| |
| /*package*/ |
| @Nullable |
| static Integer compareLocal(@NotNull Visibility first, @NotNull Visibility second) { |
| if (first == second) return 0; |
| Integer firstIndex = ORDERED_VISIBILITIES.get(first); |
| Integer secondIndex = ORDERED_VISIBILITIES.get(second); |
| if (firstIndex == null || secondIndex == null || firstIndex.equals(secondIndex)) { |
| return null; |
| } |
| return firstIndex - secondIndex; |
| } |
| |
| @Nullable |
| public static Integer compare(@NotNull Visibility first, @NotNull Visibility second) { |
| Integer result = first.compareTo(second); |
| if (result != null) { |
| return result; |
| } |
| Integer oppositeResult = second.compareTo(first); |
| if (oppositeResult != null) { |
| return -oppositeResult; |
| } |
| return null; |
| } |
| |
| public static final Visibility DEFAULT_VISIBILITY = PUBLIC; |
| |
| /** |
| * This value should be used for receiverValue parameter of Visibility.isVisible |
| * iff there is intention to determine if member is visible for any receiver. |
| */ |
| private static final ReceiverValue IRRELEVANT_RECEIVER = new ReceiverValue() { |
| @NotNull |
| @Override |
| public KotlinType getType() { |
| throw new IllegalStateException("This method should not be called"); |
| } |
| |
| @NotNull |
| @Override |
| public ReceiverValue replaceType(@NotNull KotlinType newType) { |
| throw new IllegalStateException("This method should not be called"); |
| } |
| }; |
| |
| /** |
| * This value should be used for receiverValue parameter of Visibility.isVisible |
| * iff there is intention to determine if member is visible without receiver related checks being performed. |
| */ |
| public static final ReceiverValue ALWAYS_SUITABLE_RECEIVER = new ReceiverValue() { |
| @NotNull |
| @Override |
| public KotlinType getType() { |
| throw new IllegalStateException("This method should not be called"); |
| } |
| |
| @NotNull |
| @Override |
| public ReceiverValue replaceType(@NotNull KotlinType newType) { |
| throw new IllegalStateException("This method should not be called"); |
| } |
| }; |
| |
| // This constant is not intended to use somewhere else from |
| @Deprecated |
| public static final ReceiverValue FALSE_IF_PROTECTED = new ReceiverValue() { |
| @NotNull |
| @Override |
| public KotlinType getType() { |
| throw new IllegalStateException("This method should not be called"); |
| } |
| |
| @NotNull |
| @Override |
| public ReceiverValue replaceType(@NotNull KotlinType newType) { |
| throw new IllegalStateException("This method should not be called"); |
| } |
| }; |
| |
| public static boolean isPrivate(@NotNull Visibility visibility) { |
| return visibility == PRIVATE || visibility == PRIVATE_TO_THIS; |
| } |
| |
| @NotNull |
| private static final ModuleVisibilityHelper MODULE_VISIBILITY_HELPER; |
| |
| static { |
| Iterator<ModuleVisibilityHelper> iterator = ServiceLoader.load(ModuleVisibilityHelper.class, ModuleVisibilityHelper.class.getClassLoader()).iterator(); |
| MODULE_VISIBILITY_HELPER = iterator.hasNext() ? iterator.next() : ModuleVisibilityHelper.EMPTY.INSTANCE; |
| } |
| } |