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