001    /*
002     * Copyright 2010-2013 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.jet.lang.resolve.lazy.descriptors;
018    
019    import com.google.common.base.Predicate;
020    import com.google.common.base.Predicates;
021    import com.google.common.collect.Collections2;
022    import com.google.common.collect.Lists;
023    import com.intellij.openapi.util.Computable;
024    import com.intellij.psi.PsiElement;
025    import com.intellij.util.Consumer;
026    import org.jetbrains.annotations.NotNull;
027    import org.jetbrains.annotations.Nullable;
028    import org.jetbrains.jet.lang.descriptors.*;
029    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
030    import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorBase;
031    import org.jetbrains.jet.lang.psi.*;
032    import org.jetbrains.jet.lang.resolve.AnnotationResolver;
033    import org.jetbrains.jet.lang.resolve.BindingContext;
034    import org.jetbrains.jet.lang.resolve.DescriptorResolver;
035    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
036    import org.jetbrains.jet.lang.resolve.lazy.ForceResolveUtil;
037    import org.jetbrains.jet.lang.resolve.lazy.LazyDescriptor;
038    import org.jetbrains.jet.lang.resolve.lazy.ResolveSession;
039    import org.jetbrains.jet.lang.resolve.lazy.ScopeProvider;
040    import org.jetbrains.jet.lang.resolve.lazy.data.FilteringClassLikeInfo;
041    import org.jetbrains.jet.lang.resolve.lazy.data.JetClassInfoUtil;
042    import org.jetbrains.jet.lang.resolve.lazy.data.JetClassLikeInfo;
043    import org.jetbrains.jet.lang.resolve.lazy.declarations.ClassMemberDeclarationProvider;
044    import org.jetbrains.jet.lang.resolve.lazy.storage.NotNullLazyValue;
045    import org.jetbrains.jet.lang.resolve.lazy.storage.NullableLazyValue;
046    import org.jetbrains.jet.lang.resolve.lazy.storage.StorageManager;
047    import org.jetbrains.jet.lang.resolve.name.Name;
048    import org.jetbrains.jet.lang.resolve.scopes.*;
049    import org.jetbrains.jet.lang.types.ErrorUtils;
050    import org.jetbrains.jet.lang.types.JetType;
051    import org.jetbrains.jet.lang.types.TypeConstructor;
052    import org.jetbrains.jet.lang.types.TypeUtils;
053    
054    import java.util.*;
055    
056    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassObjectName;
057    import static org.jetbrains.jet.lang.resolve.ModifiersChecker.*;
058    
059    public class LazyClassDescriptor extends ClassDescriptorBase implements LazyDescriptor, ClassDescriptor {
060    
061        private static final Predicate<Object> ONLY_ENUM_ENTRIES = Predicates.instanceOf(JetEnumEntry.class);
062        private static final Predicate<JetType> VALID_SUPERTYPE = new Predicate<JetType>() {
063            @Override
064            public boolean apply(JetType type) {
065                assert !ErrorUtils.isErrorType(type) : "Error types must be filtered out in DescriptorResolver";
066                return TypeUtils.getClassDescriptor(type) != null;
067            }
068        };
069        private final ResolveSession resolveSession;
070        private final JetClassLikeInfo originalClassInfo;
071        private final ClassMemberDeclarationProvider declarationProvider;
072    
073        private final Name name;
074        private final DeclarationDescriptor containingDeclaration;
075        private final LazyClassTypeConstructor typeConstructor;
076        private final Modality modality;
077        private final Visibility visibility;
078        private final ClassKind kind;
079        private final boolean isInner;
080    
081        private final NotNullLazyValue<ReceiverParameterDescriptor> thisAsReceiverParameter;
082        private final NotNullLazyValue<List<AnnotationDescriptor>> annotations;
083        private final NullableLazyValue<ClassDescriptor> classObjectDescriptor;
084    
085        private final LazyClassMemberScope unsubstitutedMemberScope;
086        private final JetScope unsubstitutedInnerClassesScope;
087    
088        private final NotNullLazyValue<JetScope> scopeForClassHeaderResolution;
089        private final NotNullLazyValue<JetScope> scopeForMemberDeclarationResolution;
090        private final NotNullLazyValue<JetScope> scopeForPropertyInitializerResolution;
091    
092        public LazyClassDescriptor(
093                @NotNull ResolveSession resolveSession,
094                @NotNull DeclarationDescriptor containingDeclaration,
095                @NotNull Name name,
096                @NotNull JetClassLikeInfo classLikeInfo
097        ) {
098            this.resolveSession = resolveSession;
099            this.name = name;
100    
101            if (classLikeInfo.getCorrespondingClassOrObject() != null) {
102                this.resolveSession.getTrace().record(BindingContext.CLASS, classLikeInfo.getCorrespondingClassOrObject(), this);
103            }
104    
105            this.originalClassInfo = classLikeInfo;
106            JetClassLikeInfo classLikeInfoForMembers =
107                    classLikeInfo.getClassKind() != ClassKind.ENUM_CLASS ? classLikeInfo : noEnumEntries(classLikeInfo);
108            this.declarationProvider = resolveSession.getDeclarationProviderFactory().getClassMemberDeclarationProvider(classLikeInfoForMembers);
109            this.containingDeclaration = containingDeclaration;
110    
111            this.unsubstitutedMemberScope = new LazyClassMemberScope(resolveSession, declarationProvider, this);
112            this.unsubstitutedInnerClassesScope = new InnerClassesScopeWrapper(unsubstitutedMemberScope);
113    
114            this.typeConstructor = new LazyClassTypeConstructor();
115    
116            JetModifierList modifierList = classLikeInfo.getModifierList();
117            this.kind = classLikeInfo.getClassKind();
118            if (kind.isObject()) {
119                this.modality = Modality.FINAL;
120            }
121            else {
122                Modality defaultModality = kind == ClassKind.TRAIT ? Modality.ABSTRACT : Modality.FINAL;
123                this.modality = resolveModalityFromModifiers(modifierList, defaultModality);
124            }
125            this.visibility = resolveVisibilityFromModifiers(modifierList, getDefaultClassVisibility(this));
126            this.isInner = isInnerClass(modifierList);
127    
128            StorageManager storageManager = resolveSession.getStorageManager();
129            this.thisAsReceiverParameter = storageManager.createLazyValue(new Computable<ReceiverParameterDescriptor>() {
130                @Override
131                public ReceiverParameterDescriptor compute() {
132                    return DescriptorResolver.createLazyReceiverParameterDescriptor(LazyClassDescriptor.this);
133                }
134            });
135            this.annotations = storageManager.createLazyValue(new Computable<List<AnnotationDescriptor>>() {
136                @Override
137                public List<AnnotationDescriptor> compute() {
138                    return resolveAnnotations();
139                }
140            });
141            this.classObjectDescriptor = storageManager.createNullableLazyValue(new Computable<ClassDescriptor>() {
142                @Override
143                public ClassDescriptor compute() {
144                    return computeClassObjectDescriptor();
145                }
146            });
147            this.scopeForClassHeaderResolution = storageManager.createLazyValue(new Computable<JetScope>() {
148                @Override
149                public JetScope compute() {
150                    return computeScopeForClassHeaderResolution();
151                }
152            });
153            this.scopeForMemberDeclarationResolution = storageManager.createLazyValue(new Computable<JetScope>() {
154                @Override
155                public JetScope compute() {
156                    return computeScopeForMemberDeclarationResolution();
157                }
158            });
159            this.scopeForPropertyInitializerResolution = storageManager.createLazyValue(new Computable<JetScope>() {
160                @Override
161                public JetScope compute() {
162                    return computeScopeForPropertyInitializerResolution();
163                }
164            });
165        }
166    
167        @Override
168        protected JetScope getScopeForMemberLookup() {
169            return unsubstitutedMemberScope;
170        }
171    
172        @NotNull
173        @Override
174        public JetScope getUnsubstitutedInnerClassesScope() {
175            return unsubstitutedInnerClassesScope;
176        }
177    
178        @NotNull
179        public JetScope getScopeForClassHeaderResolution() {
180            return scopeForClassHeaderResolution.compute();
181        }
182    
183        @NotNull
184        private JetScope computeScopeForClassHeaderResolution() {
185            WritableScopeImpl scope = new WritableScopeImpl(JetScope.EMPTY, this, RedeclarationHandler.DO_NOTHING, "Scope with type parameters for " + name);
186            for (TypeParameterDescriptor typeParameterDescriptor : getTypeConstructor().getParameters()) {
187                scope.addClassifierDescriptor(typeParameterDescriptor);
188            }
189            scope.changeLockLevel(WritableScope.LockLevel.READING);
190    
191            PsiElement scopeAnchor = declarationProvider.getOwnerInfo().getScopeAnchor();
192    
193            return new ChainedScope(this, "ScopeForClassHeaderResolution: " + getName(),
194                    scope,
195                    getScopeProvider().getResolutionScopeForDeclaration(scopeAnchor));
196        }
197    
198        @NotNull
199        public JetScope getScopeForMemberDeclarationResolution() {
200            return scopeForMemberDeclarationResolution.compute();
201        }
202    
203        @NotNull
204        private JetScope computeScopeForMemberDeclarationResolution() {
205            WritableScopeImpl thisScope = new WritableScopeImpl(JetScope.EMPTY, this, RedeclarationHandler.DO_NOTHING, "Scope with 'this' for " + name);
206            thisScope.addLabeledDeclaration(this);
207            thisScope.changeLockLevel(WritableScope.LockLevel.READING);
208    
209            ClassDescriptor classObject = getClassObjectDescriptor();
210            JetScope classObjectAdapterScope = (classObject != null) ? new ClassObjectMixinScope(classObject) : JetScope.EMPTY;
211    
212            return new ChainedScope(
213                    this,
214                    "ScopeForMemberDeclarationResolution: " + getName(),
215                    thisScope,
216                    getScopeForMemberLookup(),
217                    getScopeForClassHeaderResolution(),
218                    classObjectAdapterScope);
219        }
220    
221        @NotNull
222        public JetScope getScopeForPropertyInitializerResolution() {
223            return scopeForPropertyInitializerResolution.compute();
224        }
225    
226        @NotNull
227        private JetScope computeScopeForPropertyInitializerResolution() {
228            ConstructorDescriptor primaryConstructor = getUnsubstitutedPrimaryConstructor();
229            if (primaryConstructor == null) return getScopeForMemberDeclarationResolution();
230    
231            WritableScopeImpl scope = new WritableScopeImpl(JetScope.EMPTY, this, RedeclarationHandler.DO_NOTHING, "Scope with constructor parameters in " + name);
232            for (ValueParameterDescriptor valueParameterDescriptor : primaryConstructor.getValueParameters()) {
233                scope.addVariableDescriptor(valueParameterDescriptor);
234            }
235            scope.changeLockLevel(WritableScope.LockLevel.READING);
236    
237            return new ChainedScope(
238                    this,
239                    "ScopeForPropertyInitializerResolution: " + getName(),
240                    scope, getScopeForMemberDeclarationResolution());
241        }
242    
243        @NotNull
244        @Override
245        public Collection<ConstructorDescriptor> getConstructors() {
246            return unsubstitutedMemberScope.getConstructors();
247        }
248    
249        @Override
250        public ConstructorDescriptor getUnsubstitutedPrimaryConstructor() {
251            return unsubstitutedMemberScope.getPrimaryConstructor();
252        }
253    
254        @NotNull
255        @Override
256        public DeclarationDescriptor getOriginal() {
257            return this;
258        }
259    
260        @NotNull
261        @Override
262        public DeclarationDescriptor getContainingDeclaration() {
263            return containingDeclaration;
264        }
265    
266        @NotNull
267        @Override
268        public TypeConstructor getTypeConstructor() {
269            return typeConstructor;
270        }
271    
272        @Override
273        public JetType getClassObjectType() {
274            ClassDescriptor classObjectDescriptor = getClassObjectDescriptor();
275            return classObjectDescriptor == null ? null : classObjectDescriptor.getDefaultType();
276        }
277    
278        @Override
279        public ClassDescriptor getClassObjectDescriptor() {
280            return classObjectDescriptor.compute();
281        }
282    
283        @Nullable
284        private ClassDescriptor computeClassObjectDescriptor() {
285            JetClassObject classObject = declarationProvider.getOwnerInfo().getClassObject();
286    
287            JetClassLikeInfo classObjectInfo = getClassObjectInfo(classObject);
288            if (classObjectInfo != null) {
289                return new LazyClassDescriptor(resolveSession, this, getClassObjectName(getName()), classObjectInfo);
290            }
291            return null;
292        }
293    
294        @Nullable
295        private JetClassLikeInfo getClassObjectInfo(JetClassObject classObject) {
296            if (classObject != null) {
297                if (!DescriptorUtils.inStaticContext(this)) {
298                    return null;
299                }
300                JetObjectDeclaration objectDeclaration = classObject.getObjectDeclaration();
301                if (objectDeclaration != null) {
302                    return JetClassInfoUtil.createClassLikeInfo(objectDeclaration);
303                }
304            }
305            else {
306                if (getKind() == ClassKind.ENUM_CLASS) {
307                    // Enum classes always have class objects, and enum constants are their members
308                    return enumClassObjectInfo(originalClassInfo);
309                }
310            }
311            return null;
312        }
313    
314        @NotNull
315        @Override
316        public ClassKind getKind() {
317            return kind;
318        }
319    
320        @NotNull
321        @Override
322        public Modality getModality() {
323            return modality;
324        }
325    
326        @NotNull
327        @Override
328        public Visibility getVisibility() {
329            return visibility;
330        }
331    
332        @Override
333        public boolean isInner() {
334            return isInner;
335        }
336    
337        @NotNull
338        @Override
339        public ReceiverParameterDescriptor getThisAsReceiverParameter() {
340            return thisAsReceiverParameter.compute();
341        }
342    
343        @Override
344        public List<AnnotationDescriptor> getAnnotations() {
345            return annotations.compute();
346        }
347    
348        @NotNull
349        private List<AnnotationDescriptor> resolveAnnotations() {
350            JetClassLikeInfo classInfo = declarationProvider.getOwnerInfo();
351            JetModifierList modifierList = classInfo.getModifierList();
352            if (modifierList != null) {
353                AnnotationResolver annotationResolver = resolveSession.getInjector().getAnnotationResolver();
354                JetScope scopeForDeclaration = getScopeProvider().getResolutionScopeForDeclaration(classInfo.getScopeAnchor());
355                return annotationResolver.resolveAnnotations(scopeForDeclaration, modifierList, resolveSession.getTrace());
356            }
357            else {
358                return Collections.emptyList();
359            }
360        }
361    
362        @NotNull
363        @Override
364        public Name getName() {
365            return name;
366        }
367    
368        @Override
369        public String toString() {
370            return "lazy class " + getName().toString();
371        }
372    
373        @Override
374        public void forceResolveAllContents() {
375            getAnnotations();
376            getClassObjectDescriptor();
377            getClassObjectType();
378            getConstructors();
379            getContainingDeclaration();
380            getThisAsReceiverParameter();
381            getKind();
382            getModality();
383            getName();
384            getOriginal();
385            getScopeForClassHeaderResolution();
386            getScopeForMemberDeclarationResolution();
387            ForceResolveUtil.forceResolveAllContents(getScopeForMemberLookup());
388            getScopeForPropertyInitializerResolution();
389            getUnsubstitutedInnerClassesScope();
390            ForceResolveUtil.forceResolveAllContents(getTypeConstructor());
391            getUnsubstitutedPrimaryConstructor();
392            getVisibility();
393        }
394    
395        private class LazyClassTypeConstructor implements LazyDescriptor, TypeConstructor {
396            private final NotNullLazyValue<Collection<JetType>> supertypes = resolveSession.getStorageManager().createLazyValueWithPostCompute(
397                    new Computable<Collection<JetType>>() {
398                        @Override
399                        public Collection<JetType> compute() {
400                            if (resolveSession.isClassSpecial(DescriptorUtils.getFQName(LazyClassDescriptor.this))) {
401                                return Collections.emptyList();
402                            }
403                            else {
404                                JetClassOrObject classOrObject = declarationProvider.getOwnerInfo().getCorrespondingClassOrObject();
405                                if (classOrObject == null) {
406                                    return Collections.emptyList();
407                                }
408                                else {
409                                    List<JetType> allSupertypes = resolveSession.getInjector().getDescriptorResolver()
410                                            .resolveSupertypes(getScopeForClassHeaderResolution(),
411                                                               LazyClassDescriptor.this, classOrObject,
412                                                               resolveSession.getTrace());
413    
414                                    return Lists.newArrayList(Collections2.filter(allSupertypes, VALID_SUPERTYPE));
415                                }
416                            }
417                        }
418                    },
419                    new Consumer<Collection<JetType>>() {
420                        @Override
421                        public void consume(@NotNull Collection<JetType> supertypes) {
422                            findAndDisconnectLoopsInTypeHierarchy(supertypes);
423                        }
424                    });
425    
426            private final NotNullLazyValue<List<TypeParameterDescriptor>> parameters = resolveSession.getStorageManager().createLazyValue(new Computable<List<TypeParameterDescriptor>>() {
427                @Override
428                public List<TypeParameterDescriptor> compute() {
429                    JetClassLikeInfo classInfo = declarationProvider.getOwnerInfo();
430                    List<JetTypeParameter> typeParameters = classInfo.getTypeParameters();
431    
432                    List<TypeParameterDescriptor> parameters = new ArrayList<TypeParameterDescriptor>(typeParameters.size());
433                    for (int i = 0; i < typeParameters.size(); i++) {
434                        parameters.add(new LazyTypeParameterDescriptor(resolveSession, LazyClassDescriptor.this, typeParameters.get(i), i));
435                    }
436    
437                    return parameters;
438                }
439            });
440    
441            @NotNull
442            @Override
443            public List<TypeParameterDescriptor> getParameters() {
444                return parameters.compute();
445            }
446    
447            @NotNull
448            @Override
449            public Collection<JetType> getSupertypes() {
450                return supertypes.compute();
451            }
452    
453            private void findAndDisconnectLoopsInTypeHierarchy(Collection<JetType> supertypes) {
454                for (Iterator<JetType> iterator = supertypes.iterator(); iterator.hasNext(); ) {
455                    JetType supertype = iterator.next();
456                    if (isReachable(supertype.getConstructor(), this, new HashSet<TypeConstructor>())) {
457                        iterator.remove();
458                    }
459                }
460            }
461    
462            private boolean isReachable(TypeConstructor from, TypeConstructor to, Set<TypeConstructor> visited) {
463                if (!visited.add(from)) return false;
464                for (JetType supertype : from.getSupertypes()) {
465                    TypeConstructor supertypeConstructor = supertype.getConstructor();
466                    if (supertypeConstructor == to) {
467                        return true;
468                    }
469                    if (isReachable(supertypeConstructor, to, visited)) {
470                        return true;
471                    }
472                }
473                return false;
474            }
475    
476            @Override
477            public boolean isSealed() {
478                return !getModality().isOverridable();
479            }
480    
481            @Override
482            public ClassifierDescriptor getDeclarationDescriptor() {
483                return LazyClassDescriptor.this;
484            }
485    
486            @Override
487            public List<AnnotationDescriptor> getAnnotations() {
488                return Collections.emptyList(); // TODO
489            }
490    
491            @Override
492            public String toString() {
493                return LazyClassDescriptor.this.getName().toString();
494            }
495    
496            @Override
497            public void forceResolveAllContents() {
498                getAnnotations();
499                getSupertypes();
500                getParameters();
501            }
502        }
503    
504        private JetClassLikeInfo noEnumEntries(JetClassLikeInfo classLikeInfo) {
505            return new FilteringClassLikeInfo(resolveSession.getStorageManager(), classLikeInfo, Predicates.not(ONLY_ENUM_ENTRIES));
506        }
507    
508        private JetClassLikeInfo enumClassObjectInfo(JetClassLikeInfo classLikeInfo) {
509            return new FilteringClassLikeInfo(resolveSession.getStorageManager(), classLikeInfo, ONLY_ENUM_ENTRIES) {
510                @Override
511                public JetClassOrObject getCorrespondingClassOrObject() {
512                    return null;
513                }
514    
515                @NotNull
516                @Override
517                public ClassKind getClassKind() {
518                    return ClassKind.CLASS_OBJECT;
519                }
520    
521                @NotNull
522                @Override
523                public List<? extends JetParameter> getPrimaryConstructorParameters() {
524                    return Collections.emptyList();
525                }
526    
527                @NotNull
528                @Override
529                public List<JetTypeParameter> getTypeParameters() {
530                    return Collections.emptyList();
531                }
532            };
533        }
534    
535        private ScopeProvider getScopeProvider() {
536            return resolveSession.getInjector().getScopeProvider();
537        }
538    
539    }