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