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