Resolve Kotlin from Java, first drop.

This commit is contained in:
Maxim Shafirov
2011-11-11 19:57:40 +04:00
parent aa02dcd0e7
commit 627a1dbf6f
16 changed files with 842 additions and 317 deletions
+2
View File
@@ -56,5 +56,7 @@
<debugger.positionManagerFactory implementation="org.jetbrains.jet.plugin.debugger.JetPositionManagerFactory"/>
<codeInsight.implementMethod language="jet" implementationClass="org.jetbrains.jet.plugin.codeInsight.ImplementMethodsHandler"/>
<codeInsight.overrideMethod language="jet" implementationClass="org.jetbrains.jet.plugin.codeInsight.OverrideMethodsHandler"/>
<java.elementFinder implementation="org.jetbrains.jet.plugin.java.JavaElementFinder"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,122 @@
/*
* @author max
*/
package org.jetbrains.jet.plugin.java;
import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.impl.compiled.ClsClassImpl;
import com.intellij.psi.impl.compiled.ClsEnumConstantImpl;
import com.intellij.psi.impl.compiled.ClsFieldImpl;
import com.intellij.psi.impl.compiled.ClsMethodImpl;
import com.intellij.psi.impl.java.stubs.*;
import com.intellij.psi.stubs.StubBase;
import org.jetbrains.annotations.NotNull;
class ClsWrapperStubPsiFactory extends StubPsiFactory {
public static final Key<PsiElement> ORIGIN_ELEMENT = Key.create("ORIGIN_ELEMENT");
private final StubPsiFactory delegate = new ClsStubPsiFactory();
@Override
public PsiClass createClass(PsiClassStub stub) {
final PsiElement origin = ((StubBase) stub).getUserData(ORIGIN_ELEMENT);
if (origin == null) return delegate.createClass(stub);
return new ClsClassImpl(stub) {
@NotNull
@Override
public PsiElement getNavigationElement() {
return origin;
}
};
}
@Override
public PsiAnnotation createAnnotation(PsiAnnotationStub stub) {
return delegate.createAnnotation(stub);
}
@Override
public PsiClassInitializer createClassInitializer(PsiClassInitializerStub stub) {
return delegate.createClassInitializer(stub);
}
@Override
public PsiReferenceList createClassReferenceList(PsiClassReferenceListStub stub) {
return delegate.createClassReferenceList(stub);
}
@Override
public PsiField createField(PsiFieldStub stub) {
final PsiElement origin = ((StubBase) stub).getUserData(ORIGIN_ELEMENT);
if (origin == null) return delegate.createField(stub);
if (stub.isEnumConstant()) {
return new ClsEnumConstantImpl(stub) {
@NotNull
@Override
public PsiElement getNavigationElement() {
return origin;
}
};
}
else {
return new ClsFieldImpl(stub) {
@NotNull
@Override
public PsiElement getNavigationElement() {
return origin;
}
};
}
}
@Override
public PsiImportList createImportList(PsiImportListStub stub) {
return delegate.createImportList(stub);
}
@Override
public PsiImportStatementBase createImportStatement(PsiImportStatementStub stub) {
return delegate.createImportStatement(stub);
}
@Override
public PsiMethod createMethod(PsiMethodStub stub) {
final PsiElement origin = ((StubBase) stub).getUserData(ORIGIN_ELEMENT);
if (origin == null) return delegate.createMethod(stub);
return new ClsMethodImpl(stub) {
@NotNull
@Override
public PsiElement getNavigationElement() {
return origin;
}
};
}
@Override
public PsiModifierList createModifierList(PsiModifierListStub stub) {
return delegate.createModifierList(stub);
}
@Override
public PsiParameter createParameter(PsiParameterStub stub) {
return delegate.createParameter(stub);
}
@Override
public PsiParameterList createParameterList(PsiParameterListStub stub) {
return delegate.createParameterList(stub);
}
@Override
public PsiTypeParameter createTypeParameter(PsiTypeParameterStub stub) {
return delegate.createTypeParameter(stub);
}
@Override
public PsiTypeParameterList createTypeParameterList(PsiTypeParameterListStub stub) {
return delegate.createTypeParameterList(stub);
}
}
@@ -0,0 +1,220 @@
/*
* @author max
*/
package org.jetbrains.jet.plugin.java;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.intellij.openapi.compiler.ex.CompilerPathsEx;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.psi.impl.compiled.ClsFileImpl;
import com.intellij.psi.impl.file.PsiPackageImpl;
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.search.GlobalSearchScope;
import com.intellij.psi.stubs.PsiClassHolderFileStub;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.SmartList;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.codegen.ClassBuilder;
import org.jetbrains.jet.codegen.ClassBuilderFactory;
import org.jetbrains.jet.codegen.GenerationState;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetNamespace;
import org.jetbrains.jet.lang.resolve.AnalyzingUtils;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.java.JavaDefaultImports;
import org.jetbrains.jet.plugin.JetFileType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class JavaElementFinder extends PsiElementFinder {
private final Project project;
public JavaElementFinder(Project project) {
this.project = project;
}
private final static Key<PsiJavaFileStub> JAVA_API_STUB = Key.create("JAVA_API_STUB");
@Override
public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
final PsiClass[] allClasses = findClasses(qualifiedName, scope);
return allClasses.length > 0 ? allClasses[0] : null;
}
@NotNull
@Override
public PsiClass[] findClasses(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
// Backend searches for java.lang.String. Will fail with SOE if continue
if (qualifiedName.startsWith("java.")) return PsiClass.EMPTY_ARRAY;
List<PsiClass> answer = new SmartList<PsiClass>();
final List<PsiFile> filesInScope = ensureUpToDate(scope);
for (PsiFile file : filesInScope) {
final PsiJavaFileStub stub = file.getUserData(JAVA_API_STUB);
if (stub == null) continue;
final String packageName = stub.getPackageName();
if (packageName != null && qualifiedName.startsWith(packageName)) {
collectClasses(answer, qualifiedName, stub);
}
}
return answer.toArray(new PsiClass[answer.size()]);
}
@Override
public PsiPackage findPackage(@NotNull String qualifiedName) {
final List<PsiFile> psiFiles = collectProjectFiles(project, GlobalSearchScope.allScope(project));
for (PsiFile psiFile : psiFiles) {
if (psiFile instanceof JetFile) {
if (qualifiedName.equals(((JetFile) psiFile).getRootNamespace().getFQName())) {
return new PsiPackageImpl(psiFile.getManager(), qualifiedName);
}
}
}
return null;
}
@NotNull
@Override
public PsiClass[] getClasses(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
List<PsiClass> answer = new SmartList<PsiClass>();
final List<PsiFile> filesInScope = ensureUpToDate(scope);
final String qualifiedName = psiPackage.getQualifiedName();
for (PsiFile file : filesInScope) {
final PsiJavaFileStub stub = file.getUserData(JAVA_API_STUB);
if (stub == null) continue;
if (Comparing.equal(qualifiedName, stub.getPackageName())) {
for (StubElement child : stub.getChildrenStubs()) {
if (child instanceof PsiClassStub) {
answer.add((PsiClass) child.getPsi());
}
}
}
}
return answer.toArray(new PsiClass[answer.size()]);
}
private static void collectClasses(List<PsiClass> answer, String qualifiedName, StubElement<?> stub) {
if (stub instanceof PsiClassStub && qualifiedName.equals(((PsiClassStub) stub).getQualifiedName())) {
answer.add((PsiClass) stub.getPsi());
}
for (StubElement child : stub.getChildrenStubs()) {
collectClasses(answer, qualifiedName, child);
}
}
private List<PsiFile> ensureUpToDate(GlobalSearchScope scope) {
final Stack<StubElement> stubStack = new Stack<StubElement>();
final ClassBuilderFactory builderFactory = new ClassBuilderFactory() {
@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
}
};
final GenerationState state = new GenerationState(project, builderFactory) {
@Override
protected void generateNamespace(JetNamespace namespace) {
final PsiJavaFileStubImpl fileStub = new PsiJavaFileStubImpl(namespace.getFQName(), true);
PsiManager manager = PsiManager.getInstance(project);
stubStack.push(fileStub);
final PsiFile file = namespace.getContainingFile();
file.putUserData(JAVA_API_STUB, fileStub);
fileStub.setPsiFactory(new ClsWrapperStubPsiFactory());
final ClsFileImpl fakeFile = new ClsFileImpl((PsiManagerImpl) manager, new ClassFileViewProvider(manager, file.getVirtualFile())) {
@NotNull
@Override
public PsiClassHolderFileStub getStub() {
return fileStub;
}
};
fakeFile.setPhysical(false);
fileStub.setPsi(fakeFile);
try {
super.generateNamespace(namespace);
}
finally {
final StubElement pop = stubStack.pop();
assert pop == fileStub : "Unbalanced stack operations";
}
}
};
final List<PsiFile> psiFiles = collectProjectFiles(project, scope);
Collection<PsiFile> dirty = Collections2.filter(psiFiles, new Predicate<PsiFile>() {
@Override
public boolean apply(PsiFile psiFile) {
return psiFile.getUserData(JAVA_API_STUB) == null;
}
});
if (dirty.size() > 0) {
final BindingContext context = AnalyzingUtils.getInstance(JavaDefaultImports.JAVA_DEFAULT_IMPORTS).shallowAnalyzeFiles(dirty);
state.compileCorrectNamespaces(context, AnalyzingUtils.collectRootNamespaces(dirty));
state.getFactory().files();
}
return psiFiles;
}
private static List<PsiFile> collectProjectFiles(final Project project, @NotNull final GlobalSearchScope scope) {
final List<PsiFile> answer = new ArrayList<PsiFile>();
final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
final PsiManager psiManager = PsiManager.getInstance(project);
VirtualFile[] contentRoots = ProjectRootManager.getInstance(project).getContentRoots();
CompilerPathsEx.visitFiles(contentRoots, new CompilerPathsEx.FileVisitor() {
@Override
protected void acceptFile(VirtualFile file, String fileRoot, String filePath) {
final FileType fileType = fileTypeManager.getFileTypeByFile(file);
if (fileType != JetFileType.INSTANCE) return;
if (scope.accept(file)) {
final PsiFile psiFile = psiManager.findFile(file);
if (psiFile instanceof JetFile) {
answer.add(psiFile);
}
}
}
});
return answer;
}
}
@@ -0,0 +1,98 @@
/*
* @author max
*/
package org.jetbrains.jet.plugin.java;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.compiled.InnerClassSourceStrategy;
import com.intellij.psi.impl.compiled.StubBuildingVisitor;
import com.intellij.psi.stubs.StubBase;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.ClassBuilder;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import java.util.List;
public class StubClassBuilder extends ClassBuilder {
private static final InnerClassSourceStrategy<Object> EMPTY_STRATEGY = new InnerClassSourceStrategy<Object>() {
@Override
public Object findInnerClass(String s, Object o) {
return null;
}
@Override
public ClassReader readerForInnerClass(Object o) {
throw new UnsupportedOperationException("Shall not be called!");
}
};
private final StubElement parent;
private StubBuildingVisitor v;
private final Stack<StubElement> parentStack;
private boolean isNamespace = false;
public StubClassBuilder(Stack<StubElement> parentStack) {
this.parentStack = parentStack;
this.parent = parentStack.peek();
}
@Override
public ClassVisitor getVisitor() {
assert v != null : "Called before class is defined";
return v;
}
@Override
public void defineClass(PsiElement origin, int version, int access, String name, @Nullable String signature, String superName, String[] interfaces) {
assert v == null : "defineClass() called twice?";
v = new StubBuildingVisitor<Object>(null, EMPTY_STRATEGY, parent, access);
super.defineClass(origin, version, access, name, signature, superName, interfaces);
if (name.equals("namespace") || name.endsWith("/namespace")) {
isNamespace = true;
}
else {
parentStack.push(v.getResult());
}
((StubBase) v.getResult()).putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin);
}
@Override
public MethodVisitor newMethod(@Nullable PsiElement origin, int access, String name, String desc, @Nullable String signature, @Nullable String[] exceptions) {
final MethodVisitor answer = super.newMethod(origin, access, name, desc, signature, exceptions);
markLastChild(origin);
return answer;
}
@Override
public FieldVisitor newField(@Nullable PsiElement origin, int access, String name, String desc, @Nullable String signature, @Nullable Object value) {
final FieldVisitor answer = super.newField(origin, access, name, desc, signature, value);
markLastChild(origin);
return answer;
}
private void markLastChild(PsiElement origin) {
final List children = v.getResult().getChildrenStubs();
StubBase last = (StubBase) children.get(children.size() - 1);
last.putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin);
}
@Override
public void done() {
if (!isNamespace) {
final StubElement pop = parentStack.pop();
assert pop == v.getResult();
}
super.done();
}
@Override
public boolean generateCode() {
return false;
}
}