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