001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.descriptors;
018    
019    import kotlin.collections.SetsKt;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.resolve.DescriptorUtils;
023    import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
024    import org.jetbrains.kotlin.resolve.scopes.receivers.SuperCallReceiverValue;
025    import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver;
026    import org.jetbrains.kotlin.types.KotlinType;
027    import org.jetbrains.kotlin.util.ModuleVisibilityHelper;
028    import org.jetbrains.kotlin.utils.CollectionsKt;
029    
030    import java.util.*;
031    
032    public class Visibilities {
033        public static final Visibility PRIVATE = new Visibility("private", false) {
034            @Override
035            public boolean mustCheckInImports() {
036                return true;
037            }
038    
039            @Override
040            public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
041                if (DescriptorUtils.isTopLevelDeclaration(what)) {
042                    SourceFile fromContainingFile = DescriptorUtils.getContainingSourceFile(from);
043                    if (fromContainingFile != SourceFile.NO_SOURCE_FILE) {
044                        return fromContainingFile.equals(DescriptorUtils.getContainingSourceFile(what));
045                    }
046                }
047    
048                DeclarationDescriptor parent = what;
049                while (parent != null) {
050                    parent = parent.getContainingDeclaration();
051                    if ((parent instanceof ClassDescriptor && !DescriptorUtils.isCompanionObject(parent)) ||
052                        parent instanceof PackageFragmentDescriptor) {
053                        break;
054                    }
055                }
056                if (parent == null) {
057                    return false;
058                }
059                DeclarationDescriptor fromParent = from;
060                while (fromParent != null) {
061                    if (parent == fromParent) {
062                        return true;
063                    }
064                    if (fromParent instanceof PackageFragmentDescriptor) {
065                        return parent instanceof PackageFragmentDescriptor
066                               && ((PackageFragmentDescriptor) parent).getFqName().equals(((PackageFragmentDescriptor) fromParent).getFqName())
067                               && DescriptorUtils.areInSameModule(fromParent, parent);
068                    }
069                    fromParent = fromParent.getContainingDeclaration();
070                }
071                return false;
072            }
073        };
074    
075        /**
076         * This visibility is needed for the next case:
077         *  class A<in T>(t: T) {
078         *      private val t: T = t // visibility for t is PRIVATE_TO_THIS
079         *
080         *      fun test() {
081         *          val x: T = t // correct
082         *          val y: T = this.t // also correct
083         *      }
084         *      fun foo(a: A<String>) {
085         *         val x: String = a.t // incorrect, because a.t can be Any
086         *      }
087         *  }
088         */
089        public static final Visibility PRIVATE_TO_THIS = new Visibility("private_to_this", false) {
090            @Override
091            public boolean isVisible(@Nullable ReceiverValue thisObject, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
092                if (PRIVATE.isVisible(thisObject, what, from)) {
093                    // See Visibility.isVisible contract
094                    if (thisObject == ALWAYS_SUITABLE_RECEIVER) return true;
095                    if (thisObject == IRRELEVANT_RECEIVER) return false;
096    
097                    DeclarationDescriptor classDescriptor = DescriptorUtils.getParentOfType(what, ClassDescriptor.class);
098    
099                    if (classDescriptor != null && thisObject instanceof ThisClassReceiver) {
100                        return ((ThisClassReceiver) thisObject).getClassDescriptor().getOriginal().equals(classDescriptor.getOriginal());
101                    }
102                }
103                return false;
104            }
105    
106            @Override
107            public boolean mustCheckInImports() {
108                return true;
109            }
110    
111            @NotNull
112            @Override
113            public String getDisplayName() {
114                return "private/*private to this*/";
115            }
116        };
117    
118        public static final Visibility PROTECTED = new Visibility("protected", true) {
119            @Override
120            public boolean mustCheckInImports() {
121                return false;
122            }
123    
124            @Override
125            public boolean isVisible(
126                    @Nullable ReceiverValue receiver,
127                    @NotNull DeclarationDescriptorWithVisibility what,
128                    @NotNull DeclarationDescriptor from
129            ) {
130                ClassDescriptor givenDescriptorContainingClass = DescriptorUtils.getParentOfType(what, ClassDescriptor.class);
131                ClassDescriptor fromClass = DescriptorUtils.getParentOfType(from, ClassDescriptor.class, false);
132                if (fromClass == null) return false;
133    
134                if (givenDescriptorContainingClass != null && DescriptorUtils.isCompanionObject(givenDescriptorContainingClass)) {
135                    // Access to protected members inside companion is allowed to all subclasses
136                    // Receiver type does not matter because objects are final
137                    // NB: protected fake overrides in companion from super class should also be allowed
138                    ClassDescriptor companionOwner = DescriptorUtils.getParentOfType(givenDescriptorContainingClass, ClassDescriptor.class);
139                    if (companionOwner != null && DescriptorUtils.isSubclass(fromClass, companionOwner)) return true;
140                }
141    
142                // The rest part of method checks visibility similarly to Java does for protected (see JLS p.6.6.2)
143    
144                // Protected fake overrides can have only one protected overridden (as protected is not allowed for interface members)
145                DeclarationDescriptorWithVisibility whatDeclaration = DescriptorUtils.unwrapFakeOverrideToAnyDeclaration(what);
146    
147                ClassDescriptor classDescriptor = DescriptorUtils.getParentOfType(whatDeclaration, ClassDescriptor.class);
148                if (classDescriptor == null) return false;
149    
150                if (DescriptorUtils.isSubclass(fromClass, classDescriptor)
151                        && doesReceiverFitForProtectedVisibility(receiver, whatDeclaration, fromClass)) {
152                    return true;
153                }
154    
155                return isVisible(receiver, what, fromClass.getContainingDeclaration());
156            }
157    
158            private boolean doesReceiverFitForProtectedVisibility(
159                    @Nullable ReceiverValue receiver,
160                    @NotNull DeclarationDescriptorWithVisibility whatDeclaration,
161                    @NotNull ClassDescriptor fromClass
162            ) {
163                //noinspection deprecation
164                if (receiver == FALSE_IF_PROTECTED) return false;
165    
166                // Do not check receiver for non-callable declarations
167                if (!(whatDeclaration instanceof CallableMemberDescriptor)) return true;
168                // Constructor accessibility check is performed manually
169                if (whatDeclaration instanceof ConstructorDescriptor) return true;
170    
171                // See Visibility.isVisible contract
172                if (receiver == ALWAYS_SUITABLE_RECEIVER) return true;
173                if (receiver == IRRELEVANT_RECEIVER || receiver == null) return false;
174    
175                KotlinType actualReceiverType = receiver instanceof SuperCallReceiverValue
176                                                ? ((SuperCallReceiverValue) receiver).getThisType()
177                                                : receiver.getType();
178    
179                return DescriptorUtils.isSubtypeOfClass(actualReceiverType, fromClass);
180            }
181        };
182    
183        public static final Visibility INTERNAL = new Visibility("internal", false) {
184            @Override
185            public boolean mustCheckInImports() {
186                return true;
187            }
188    
189            @Override
190            public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
191                DeclarationDescriptor fromOrModule = from instanceof PackageViewDescriptor ? ((PackageViewDescriptor) from).getModule() : from;
192                if (!DescriptorUtils.getContainingModule(what).isFriend(DescriptorUtils.getContainingModule(fromOrModule))) return false;
193    
194                return MODULE_VISIBILITY_HELPER.isInFriendModule(what, from);
195            }
196        };
197    
198        public static final Visibility PUBLIC = new Visibility("public", true) {
199            @Override
200            public boolean mustCheckInImports() {
201                return false;
202            }
203    
204            @Override
205            public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
206                return true;
207            }
208        };
209    
210        public static final Visibility LOCAL = new Visibility("local", false) {
211            @Override
212            public boolean mustCheckInImports() {
213                throw new IllegalStateException("This method shouldn't be invoked for LOCAL visibility");
214            }
215    
216            @Override
217            public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
218                throw new IllegalStateException("This method shouldn't be invoked for LOCAL visibility");
219            }
220        };
221    
222        public static final Visibility INHERITED = new Visibility("inherited", false) {
223            @Override
224            public boolean mustCheckInImports() {
225                throw new IllegalStateException("This method shouldn't be invoked for INHERITED visibility");
226            }
227    
228            @Override
229            public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
230                throw new IllegalStateException("Visibility is unknown yet"); //This method shouldn't be invoked for INHERITED visibility
231            }
232        };
233    
234        /* Visibility for fake override invisible members (they are created for better error reporting) */
235        public static final Visibility INVISIBLE_FAKE = new Visibility("invisible_fake", false) {
236            @Override
237            public boolean mustCheckInImports() {
238                throw new IllegalStateException("This method shouldn't be invoked for INVISIBLE_FAKE visibility");
239            }
240    
241            @Override
242            public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
243                return false;
244            }
245        };
246    
247        // Currently used as default visibility of FunctionDescriptor
248        // It's needed to prevent NPE when requesting non-nullable visibility of descriptor before `initialize` has been called
249        public static final Visibility UNKNOWN = new Visibility("unknown", false) {
250            @Override
251            public boolean mustCheckInImports() {
252                throw new IllegalStateException("This method shouldn't be invoked for UNKNOWN visibility");
253            }
254    
255            @Override
256            public boolean isVisible(
257                    @Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from
258            ) {
259                return false;
260            }
261        };
262    
263        public static final Set<Visibility> INVISIBLE_FROM_OTHER_MODULES =
264                Collections.unmodifiableSet(SetsKt.setOf(PRIVATE, PRIVATE_TO_THIS, INTERNAL, LOCAL));
265    
266        private Visibilities() {
267        }
268    
269        public static boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
270            return findInvisibleMember(receiver, what, from) == null;
271        }
272    
273        /**
274         * @see Visibility.isVisible contract
275         */
276        public static boolean isVisibleIgnoringReceiver(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
277            return findInvisibleMember(ALWAYS_SUITABLE_RECEIVER, what, from) == null;
278        }
279    
280        /**
281         * @see Visibility.isVisible contract
282         * @see Visibilities.RECEIVER_DOES_NOT_EXIST
283         */
284        public static boolean isVisibleWithAnyReceiver(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
285            return findInvisibleMember(IRRELEVANT_RECEIVER, what, from) == null;
286        }
287    
288        @Nullable
289        public static DeclarationDescriptorWithVisibility findInvisibleMember(
290                @Nullable ReceiverValue receiver,
291                @NotNull DeclarationDescriptorWithVisibility what,
292                @NotNull DeclarationDescriptor from
293        ) {
294            DeclarationDescriptorWithVisibility parent = (DeclarationDescriptorWithVisibility) what.getOriginal();
295            while (parent != null && parent.getVisibility() != LOCAL) {
296                if (!parent.getVisibility().isVisible(receiver, parent, from)) {
297                    return parent;
298                }
299                parent = DescriptorUtils.getParentOfType(parent, DeclarationDescriptorWithVisibility.class);
300            }
301            return null;
302        }
303    
304        private static final Map<Visibility, Integer> ORDERED_VISIBILITIES;
305    
306        static {
307            Map<Visibility, Integer> visibilities = CollectionsKt.newHashMapWithExpectedSize(4);
308            visibilities.put(PRIVATE_TO_THIS, 0);
309            visibilities.put(PRIVATE, 0);
310            visibilities.put(INTERNAL, 1);
311            visibilities.put(PROTECTED, 1);
312            visibilities.put(PUBLIC, 2);
313            ORDERED_VISIBILITIES = Collections.unmodifiableMap(visibilities);
314        }
315    
316        /*package*/
317        @Nullable
318        static Integer compareLocal(@NotNull Visibility first, @NotNull Visibility second) {
319            if (first == second) return 0;
320            Integer firstIndex = ORDERED_VISIBILITIES.get(first);
321            Integer secondIndex = ORDERED_VISIBILITIES.get(second);
322            if (firstIndex == null || secondIndex == null || firstIndex.equals(secondIndex)) {
323                return null;
324            }
325            return firstIndex - secondIndex;
326        }
327    
328        @Nullable
329        public static Integer compare(@NotNull Visibility first, @NotNull Visibility second) {
330            Integer result = first.compareTo(second);
331            if (result != null) {
332                return result;
333            }
334            Integer oppositeResult = second.compareTo(first);
335            if (oppositeResult != null) {
336                return -oppositeResult;
337            }
338            return null;
339        }
340    
341        public static final Visibility DEFAULT_VISIBILITY = PUBLIC;
342    
343        /**
344         * This value should be used for receiverValue parameter of Visibility.isVisible
345         * iff there is intention to determine if member is visible for any receiver.
346         */
347        private static final ReceiverValue IRRELEVANT_RECEIVER = new ReceiverValue() {
348            @NotNull
349            @Override
350            public KotlinType getType() {
351                throw new IllegalStateException("This method should not be called");
352            }
353        };
354    
355        /**
356         * This value should be used for receiverValue parameter of Visibility.isVisible
357         * iff there is intention to determine if member is visible without receiver related checks being performed.
358         */
359        public static final ReceiverValue ALWAYS_SUITABLE_RECEIVER = new ReceiverValue() {
360            @NotNull
361            @Override
362            public KotlinType getType() {
363                throw new IllegalStateException("This method should not be called");
364            }
365        };
366    
367        // This constant is not intended to use somewhere else from
368        @Deprecated
369        public static final ReceiverValue FALSE_IF_PROTECTED = new ReceiverValue() {
370            @NotNull
371            @Override
372            public KotlinType getType() {
373                throw new IllegalStateException("This method should not be called");
374            }
375        };
376    
377        public static boolean isPrivate(@NotNull Visibility visibility) {
378            return visibility == PRIVATE || visibility == PRIVATE_TO_THIS;
379        }
380    
381        @NotNull
382        private static final ModuleVisibilityHelper MODULE_VISIBILITY_HELPER;
383    
384        static {
385            Iterator<ModuleVisibilityHelper> iterator = ServiceLoader.load(ModuleVisibilityHelper.class, ModuleVisibilityHelper.class.getClassLoader()).iterator();
386            MODULE_VISIBILITY_HELPER = iterator.hasNext() ? iterator.next() : ModuleVisibilityHelper.EMPTY.INSTANCE;
387        }
388    }