001    /*
002     * Copyright 2010-2014 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;
018    
019    import com.google.common.collect.Lists;
020    import com.google.common.collect.Sets;
021    import kotlin.Function0;
022    import kotlin.Function1;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.annotations.TestOnly;
026    import org.jetbrains.jet.lang.descriptors.*;
027    import org.jetbrains.jet.lang.psi.JetFile;
028    import org.jetbrains.jet.lang.psi.JetImportDirective;
029    import org.jetbrains.jet.lang.resolve.BindingTrace;
030    import org.jetbrains.jet.lang.resolve.Importer;
031    import org.jetbrains.jet.lang.resolve.ImportsResolver;
032    import org.jetbrains.jet.lang.resolve.JetModuleUtil;
033    import org.jetbrains.jet.lang.resolve.name.LabelName;
034    import org.jetbrains.jet.lang.resolve.name.Name;
035    import org.jetbrains.jet.lang.resolve.scopes.*;
036    import org.jetbrains.jet.storage.MemoizedFunctionToNotNull;
037    import org.jetbrains.jet.utils.Printer;
038    
039    import java.util.Collection;
040    import java.util.Collections;
041    import java.util.List;
042    import java.util.Set;
043    
044    import static org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode;
045    
046    public class LazyImportScope implements JetScope, LazyEntity {
047        private final ResolveSession resolveSession;
048        private final PackageViewDescriptor packageDescriptor;
049        private final ImportsProvider importsProvider;
050        private final JetScope rootScope;
051        private final BindingTrace traceForImportResolve;
052        private final String debugName;
053    
054        private static class ImportResolveStatus {
055            private final LookupMode lookupMode;
056            private final JetScope scope;
057    
058            ImportResolveStatus(LookupMode lookupMode, JetScope scope) {
059                this.lookupMode = lookupMode;
060                this.scope = scope;
061            }
062        }
063    
064        private class ImportDirectiveResolveCache {
065            private final JetImportDirective directive;
066    
067            @Nullable
068            private volatile ImportResolveStatus importResolveStatus;
069    
070            private ImportDirectiveResolveCache(JetImportDirective directive) {
071                this.directive = directive;
072            }
073    
074            private JetScope scopeForMode(final LookupMode mode) {
075                ImportResolveStatus status = importResolveStatus;
076                if (status != null && (status.lookupMode == mode || status.lookupMode == LookupMode.EVERYTHING)) {
077                    return status.scope;
078                }
079    
080                return resolveSession.getStorageManager().compute(new Function0<JetScope>() {
081                    @Override
082                    public JetScope invoke() {
083                        ImportResolveStatus cachedStatus = importResolveStatus;
084                        if (cachedStatus != null && (cachedStatus.lookupMode == mode || cachedStatus.lookupMode == LookupMode.EVERYTHING)) {
085                            return cachedStatus.scope;
086                        }
087    
088                        WritableScope directiveImportScope = new WritableScopeImpl(
089                                JetScope.EMPTY, packageDescriptor, RedeclarationHandler.DO_NOTHING,
090                                "Scope for import '" + directive.getText() + "' resolve in " + toString());
091                        directiveImportScope.changeLockLevel(WritableScope.LockLevel.BOTH);
092    
093                        Importer.StandardImporter importer = new Importer.StandardImporter(directiveImportScope);
094                        directiveUnderResolve = directive;
095    
096                        try {
097                            resolveSession.getQualifiedExpressionResolver().processImportReference(
098                                    directive,
099                                    rootScope,
100                                    packageDescriptor.getMemberScope(),
101                                    importer,
102                                    traceForImportResolve,
103                                    resolveSession.getModuleDescriptor(),
104                                    mode);
105                        }
106                        finally {
107                            directiveUnderResolve = null;
108                            directiveImportScope.changeLockLevel(WritableScope.LockLevel.READING);
109                        }
110    
111                        importResolveStatus = new ImportResolveStatus(mode, directiveImportScope);
112                        return directiveImportScope;
113                    }
114                });
115            }
116        }
117    
118        private final MemoizedFunctionToNotNull<JetImportDirective, ImportDirectiveResolveCache> importedScopesProvider;
119    
120        private JetImportDirective directiveUnderResolve = null;
121    
122        public LazyImportScope(
123                @NotNull ResolveSession resolveSession,
124                @NotNull PackageViewDescriptor packageDescriptor,
125                @NotNull List<JetImportDirective> imports,
126                @NotNull BindingTrace traceForImportResolve,
127                @NotNull String debugName,
128                boolean inRootPackage
129        ) {
130            this.resolveSession = resolveSession;
131            this.packageDescriptor = packageDescriptor;
132            this.importsProvider = new ImportsProvider(resolveSession.getStorageManager(), imports);
133            this.traceForImportResolve = traceForImportResolve;
134            this.debugName = debugName;
135    
136            this.importedScopesProvider = resolveSession.getStorageManager().createMemoizedFunction(new Function1<JetImportDirective, ImportDirectiveResolveCache>() {
137                @Override
138                public ImportDirectiveResolveCache invoke(JetImportDirective directive) {
139                    return new ImportDirectiveResolveCache(directive);
140                }
141            });
142    
143            this.rootScope = JetModuleUtil.getImportsResolutionScope(resolveSession.getModuleDescriptor(), inRootPackage);
144        }
145    
146        public static LazyImportScope createImportScopeForFile(
147                @NotNull ResolveSession resolveSession,
148                @NotNull PackageViewDescriptor packageDescriptor,
149                @NotNull JetFile jetFile,
150                @NotNull BindingTrace traceForImportResolve,
151                @NotNull String debugName
152        ) {
153            return new LazyImportScope(
154                    resolveSession,
155                    packageDescriptor,
156                    Lists.reverse(jetFile.getImportDirectives()),
157                    traceForImportResolve,
158                    debugName,
159                    packageDescriptor.getFqName().isRoot());
160        }
161    
162        @Override
163        public void forceResolveAllContents() {
164            for (JetImportDirective importDirective : importsProvider.getAllImports()) {
165                getImportScope(importDirective, LookupMode.EVERYTHING);
166            }
167        }
168    
169        @Nullable
170        private <D extends DeclarationDescriptor> D selectFirstFromImports(
171                final Name name,
172                final LookupMode lookupMode,
173                final JetScopeSelectorUtil.ScopeByNameSelector<D> descriptorSelector
174        ) {
175            return resolveSession.getStorageManager().compute(new Function0<D>() {
176                @Override
177                public D invoke() {
178                    for (JetImportDirective directive : importsProvider.getImports(name)) {
179                        if (directive == directiveUnderResolve) {
180                            // This is the recursion in imports analysis
181                            return null;
182                        }
183    
184                        D foundDescriptor = descriptorSelector.get(getImportScope(directive, lookupMode), name);
185                        if (foundDescriptor != null) {
186                            return foundDescriptor;
187                        }
188                    }
189    
190                    return null;
191                }
192            });
193        }
194    
195        @NotNull
196        private <D extends DeclarationDescriptor> Collection<D> collectFromImports(
197                final Name name,
198                final LookupMode lookupMode,
199                final JetScopeSelectorUtil.ScopeByNameMultiSelector<D> descriptorsSelector
200        ) {
201            return resolveSession.getStorageManager().compute(new Function0<Collection<D>>() {
202                @Override
203                public Collection<D> invoke() {
204                    Set<D> descriptors = Sets.newHashSet();
205                    for (JetImportDirective directive : importsProvider.getImports(name)) {
206                        if (directive == directiveUnderResolve) {
207                            // This is the recursion in imports analysis
208                            throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
209                        }
210    
211                        descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode), name));
212                    }
213    
214                    return descriptors;
215                }
216            });
217        }
218    
219        @NotNull
220        private <D extends DeclarationDescriptor> Collection<D> collectFromImports(
221                final LookupMode lookupMode,
222                final JetScopeSelectorUtil.ScopeDescriptorSelector<D> descriptorsSelector
223        ) {
224            return resolveSession.getStorageManager().compute(new Function0<Collection<D>>() {
225                @Override
226                public Collection<D> invoke() {
227                    Set<D> descriptors = Sets.newHashSet();
228                    for (JetImportDirective directive : importsProvider.getAllImports()) {
229                        if (directive == directiveUnderResolve) {
230                            // This is the recursion in imports analysis
231                            throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
232                        }
233    
234                        descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode)));
235                    }
236    
237                    return descriptors;
238                }
239            });
240        }
241    
242        @NotNull
243        private JetScope getImportScope(JetImportDirective directive, LookupMode lookupMode) {
244            return importedScopesProvider.invoke(directive).scopeForMode(lookupMode);
245        }
246    
247        @Nullable
248        @Override
249        public ClassifierDescriptor getClassifier(@NotNull Name name) {
250            return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.CLASSIFIER_DESCRIPTOR_SCOPE_SELECTOR);
251        }
252    
253        @Nullable
254        @Override
255        public PackageViewDescriptor getPackage(@NotNull Name name) {
256            return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.PACKAGE_SCOPE_SELECTOR);
257        }
258    
259        @NotNull
260        @Override
261        public Collection<VariableDescriptor> getProperties(@NotNull Name name) {
262            return collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_PROPERTIES_SCOPE_SELECTOR);
263        }
264    
265        @Nullable
266        @Override
267        public VariableDescriptor getLocalVariable(@NotNull Name name) {
268            return null;
269        }
270    
271        @NotNull
272        @Override
273        public Collection<FunctionDescriptor> getFunctions(@NotNull Name name) {
274            return collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_FUNCTION_SCOPE_SELECTOR);
275        }
276    
277        @NotNull
278        @Override
279        public DeclarationDescriptor getContainingDeclaration() {
280            return packageDescriptor;
281        }
282    
283        @NotNull
284        @Override
285        public Collection<DeclarationDescriptor> getDeclarationsByLabel(@NotNull LabelName labelName) {
286            return Collections.emptyList();
287        }
288    
289        @NotNull
290        @Override
291        public Collection<DeclarationDescriptor> getAllDescriptors() {
292            return collectFromImports(LookupMode.EVERYTHING, JetScopeSelectorUtil.ALL_DESCRIPTORS_SCOPE_SELECTOR);
293        }
294    
295        @NotNull
296        @Override
297        public List<ReceiverParameterDescriptor> getImplicitReceiversHierarchy() {
298            return Collections.emptyList();
299        }
300    
301        @NotNull
302        @Override
303        public Collection<DeclarationDescriptor> getOwnDeclaredDescriptors() {
304            return Collections.emptyList();
305        }
306    
307        @Override
308        public String toString() {
309            return "LazyImportScope: " + debugName;
310        }
311    
312        @TestOnly
313        @Override
314        public void printScopeStructure(@NotNull Printer p) {
315            p.println(getClass().getSimpleName(), ": ", debugName, " {");
316            p.pushIndent();
317    
318            p.println("packageDescriptor = ", packageDescriptor);
319    
320            p.print("rootScope = ");
321            rootScope.printScopeStructure(p.withholdIndentOnce());
322    
323            p.popIndent();
324            p.println("}");
325        }
326    }