/*
* 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() {
return true;
}
@
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");
}
@
NotNull
@
Override
public
ReceiverValue getOriginal() {
return this;
}
};
/**
* 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");
}
@
NotNull
@
Override
public
ReceiverValue getOriginal() {
return this;
}
};
// 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");
}
@
NotNull
@
Override
public
ReceiverValue getOriginal() {
return this;
}
};
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;
}
}