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