forked from JetBrains/kotlin
/
JetLightClass.java
251 lines (213 loc) · 9.12 KB
/
JetLightClass.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/*
* Copyright 2010-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @author max
*/
package org.jetbrains.jet.asJava;
import com.intellij.navigation.ItemPresentation;
import com.intellij.navigation.ItemPresentationProviders;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.psi.impl.compiled.ClsFileImpl;
import com.intellij.psi.impl.java.stubs.PsiClassStub;
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub;
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl;
import com.intellij.psi.impl.light.AbstractLightClass;
import com.intellij.psi.stubs.PsiClassHolderFileStub;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.util.*;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.analyzer.AnalyzeExhaust;
import org.jetbrains.jet.codegen.ClassBuilder;
import org.jetbrains.jet.codegen.ClassBuilderFactory;
import org.jetbrains.jet.codegen.ClassBuilderMode;
import org.jetbrains.jet.codegen.CompilationErrorHandler;
import org.jetbrains.jet.codegen.GenerationState;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetFunction;
import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.java.*;
import org.jetbrains.jet.plugin.JetLanguage;
import org.jetbrains.jet.util.QualifiedNamesUtil;
import javax.swing.*;
import java.util.Collections;
public class JetLightClass extends AbstractLightClass implements JetJavaMirrorMarker {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jet.asJava.JetLightClass");
private final static Key<CachedValue<PsiJavaFileStub>> JAVA_API_STUB = Key.create("JAVA_API_STUB");
private final JetFile file;
private final FqName qualifiedName;
private PsiClass delegate;
public JetLightClass(PsiManager manager, JetFile file, FqName qualifiedName) {
super(manager, JetLanguage.INSTANCE);
this.file = file;
this.qualifiedName = qualifiedName;
}
@Override
public String getName() {
return QualifiedNamesUtil.fqnToShortName(qualifiedName).getName();
}
@Override
public PsiElement copy() {
return new JetLightClass(getManager(), file, qualifiedName);
}
@NotNull
@Override
public PsiClass getDelegate() {
if (delegate == null) {
delegate = findClass(qualifiedName, getStub());
if (delegate == null) {
throw new IllegalStateException("Class not found for qualified name: " + qualifiedName);
}
}
return delegate;
}
@Override
public PsiFile getContainingFile() {
return file;
}
private static PsiClass findClass(FqName fqn, StubElement<?> stub) {
if (stub instanceof PsiClassStub && Comparing.equal(fqn.getFqName(), ((PsiClassStub)stub).getQualifiedName())) {
return (PsiClass)stub.getPsi();
}
for (StubElement child : stub.getChildrenStubs()) {
PsiClass answer = findClass(fqn, child);
if (answer != null) return answer;
}
return null;
}
@Override
public String getQualifiedName() {
return qualifiedName.getFqName();
}
public FqName getFqName() {
return qualifiedName;
}
private PsiJavaFileStub getStub() {
CachedValue<PsiJavaFileStub> answer = file.getUserData(JAVA_API_STUB);
if (answer == null) {
answer = CachedValuesManager.getManager(getProject()).createCachedValue(new CachedValueProvider<PsiJavaFileStub>() {
@Override
public Result<PsiJavaFileStub> compute() {
return Result.create(calcStub(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
}
}, false);
file.putUserData(JAVA_API_STUB, answer);
}
return answer.getValue();
}
private PsiJavaFileStub calcStub() {
final PsiJavaFileStubImpl answer = new PsiJavaFileStubImpl(JetPsiUtil.getFQName(file).getFqName(), true);
final Project project = getProject();
final Stack<StubElement> stubStack = new Stack<StubElement>();
final ClassBuilderFactory builderFactory = new ClassBuilderFactory() {
@NotNull
@Override
public ClassBuilderMode getClassBuilderMode() {
return ClassBuilderMode.SIGNATURES;
}
@Override
public ClassBuilder newClassBuilder() {
return new StubClassBuilder(stubStack);
}
@Override
public String asText(ClassBuilder builder) {
throw new UnsupportedOperationException("asText is not implemented"); // TODO
}
@Override
public byte[] asBytes(ClassBuilder builder) {
throw new UnsupportedOperationException("asBytes is not implemented"); // TODO
}
};
// The context must reflect _all files in the module_. not only the current file
// Otherwise, the analyzer gets confused and can't, for example, tell which files come as sources and which
// must be loaded from .class files
AnalyzeExhaust context = AnalyzerFacadeForJVM.shallowAnalyzeFiles(
JetFilesProvider.getInstance(project).sampleToAllFilesInModule().fun(file),
// TODO: wrong environment // stepan.koltsov@ 2012-04-09
CompilerSpecialMode.REGULAR, CompilerDependencies.compilerDependenciesForProduction(CompilerSpecialMode.REGULAR));
if (context.isError()) {
throw new IllegalStateException("failed to analyze: " + context.getError(), context.getError());
}
final GenerationState state = new GenerationState(project, builderFactory, context, Collections.singletonList(file)) {
@Override
protected void generateNamespace(JetFile namespace) {
PsiManager manager = PsiManager.getInstance(project);
stubStack.push(answer);
answer.setPsiFactory(new ClsWrapperStubPsiFactory());
final ClsFileImpl fakeFile =
new ClsFileImpl((PsiManagerImpl)manager, new ClassFileViewProvider(manager, file.getVirtualFile())) {
@NotNull
@Override
public PsiClassHolderFileStub getStub() {
return answer;
}
};
fakeFile.setPhysical(false);
answer.setPsi(fakeFile);
super.generateNamespace(namespace);
final StubElement pop = stubStack.pop();
if (pop != answer) {
LOG.error("Unbalanced stack operations: " + pop);
}
}
};
state.compileCorrectFiles(CompilationErrorHandler.THROW_EXCEPTION);
state.getFactory().files();
return answer;
}
@Override
public ItemPresentation getPresentation() {
return ItemPresentationProviders.getItemPresentation(this);
}
@Override
public boolean isEquivalentTo(PsiElement another) {
return another instanceof PsiClass && Comparing.equal(((PsiClass)another).getQualifiedName(), getQualifiedName());
}
@Override
public Icon getElementIcon(final int flags) {
return PsiClassImplUtil.getClassIcon(flags, this);
}
@Override
public String toString() {
try {
return JetLightClass.class.getSimpleName() + ":" + getQualifiedName();
}
catch (Throwable e) {
return JetLightClass.class.getSimpleName() + ":" + e.toString();
}
}
public static JetLightClass wrapDelegate(JetClass jetClass) {
return new JetLightClass(jetClass.getManager(), (JetFile) jetClass.getContainingFile(), JetPsiUtil.getFQName(jetClass));
}
public static PsiMethod wrapMethod(JetFunction function) {
JetClass containingClass = PsiTreeUtil.getParentOfType(function, JetClass.class);
JetLightClass wrapper = wrapDelegate(containingClass);
for (PsiMethod method : wrapper.getMethods()) {
if (method instanceof PsiCompiledElement && ((PsiCompiledElement) method).getMirror() == function) {
return method;
}
}
return null;
}
}