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