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 ClassDescriptor getObjectDescriptor(@NotNull Name name) {
250 return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.NAMED_OBJECT_SCOPE_SELECTOR);
251 }
252
253 @NotNull
254 @Override
255 public Collection<ClassDescriptor> getObjectDescriptors() {
256 return collectFromImports(LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.OBJECTS_SCOPE_SELECTOR);
257 }
258
259 @Nullable
260 @Override
261 public NamespaceDescriptor getNamespace(@NotNull Name name) {
262 return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.NAMESPACE_SCOPE_SELECTOR);
263 }
264
265 @NotNull
266 @Override
267 public Collection<VariableDescriptor> getProperties(@NotNull Name name) {
268 return collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_PROPERTIES_SCOPE_SELECTOR);
269 }
270
271 @Nullable
272 @Override
273 public VariableDescriptor getLocalVariable(@NotNull Name name) {
274 return null;
275 }
276
277 @NotNull
278 @Override
279 public Collection<FunctionDescriptor> getFunctions(@NotNull Name name) {
280 return collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_FUNCTION_SCOPE_SELECTOR);
281 }
282
283 @NotNull
284 @Override
285 public DeclarationDescriptor getContainingDeclaration() {
286 return packageDescriptor;
287 }
288
289 @NotNull
290 @Override
291 public Collection<DeclarationDescriptor> getDeclarationsByLabel(@NotNull LabelName labelName) {
292 return Collections.emptyList();
293 }
294
295 @NotNull
296 @Override
297 public Collection<DeclarationDescriptor> getAllDescriptors() {
298 return collectFromImports(LookupMode.EVERYTHING, JetScopeSelectorUtil.ALL_DESCRIPTORS_SCOPE_SELECTOR);
299 }
300
301 @NotNull
302 @Override
303 public List<ReceiverParameterDescriptor> getImplicitReceiversHierarchy() {
304 return Collections.emptyList();
305 }
306
307 @NotNull
308 @Override
309 public Collection<DeclarationDescriptor> getOwnDeclaredDescriptors() {
310 return Collections.emptyList();
311 }
312
313 @Override
314 public String toString() {
315 return "LazyImportScope: " + debugName;
316 }
317
318 @TestOnly
319 @Override
320 public void printScopeStructure(@NotNull Printer p) {
321 p.println(getClass().getSimpleName(), ": ", debugName, " {");
322 p.pushIndent();
323
324 p.println("packageDescriptor = ", packageDescriptor);
325
326 p.print("rootScope = ");
327 rootScope.printScopeStructure(p.withholdIndentOnce());
328
329 p.popIndent();
330 p.println("}");
331 }
332 }