Add support of light classes for local class/object declarations

This commit is contained in:
Alexey Sedunov
2013-11-28 17:24:55 +04:00
parent 594c966c44
commit 6913f42a97
20 changed files with 356 additions and 85 deletions
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2013 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.
*/
package org.jetbrains.jet.asJava
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.resolve.BindingContext
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
data class GeneratedLightClassData(
val javaFileStub: PsiJavaFileStub,
val jvmInternalName: String,
val descriptor: ClassDescriptor?
)
@@ -37,20 +37,24 @@ import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.Type;
import org.jetbrains.jet.codegen.CompilationErrorHandler;
import org.jetbrains.jet.codegen.NamespaceCodegen;
import org.jetbrains.jet.codegen.binding.CodegenBinding;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.Progress;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.types.lang.InlineUtil;
import java.util.Collection;
import java.util.Collections;
public class KotlinJavaFileStubProvider implements CachedValueProvider<PsiJavaFileStub> {
public class KotlinJavaFileStubProvider implements CachedValueProvider<GeneratedLightClassData> {
@NotNull
public static KotlinJavaFileStubProvider createForPackageClass(
@@ -58,90 +62,131 @@ public class KotlinJavaFileStubProvider implements CachedValueProvider<PsiJavaFi
@NotNull final FqName packageFqName,
@NotNull final GlobalSearchScope searchScope
) {
return new KotlinJavaFileStubProvider(project, new StubGenerationStrategy.NoDeclaredClasses() {
return new KotlinJavaFileStubProvider(
project,
false,
new StubGenerationStrategy.NoDeclaredClasses() {
@NotNull
@Override
public LightClassConstructionContext createLightClassConstructionContext(@NotNull Collection<JetFile> files) {
return LightClassGenerationSupport.getInstance(project).analyzeRelevantCode(files);
}
@NotNull
@Override
public Collection<JetFile> getFiles() {
// Don't memoize this, it can be called again after an out-of-code-block modification occurs,
// and the set of files changes
return LightClassGenerationSupport.getInstance(project).findFilesForPackage(packageFqName, searchScope);
}
@NotNull
@Override
public Collection<JetFile> getFiles() {
// Don't memoize this, it can be called again after an out-of-code-block modification occurs,
// and the set of files changes
return LightClassGenerationSupport.getInstance(project).findFilesForPackage(packageFqName, searchScope);
}
@NotNull
@Override
public FqName getPackageFqName() {
return packageFqName;
}
@NotNull
@Override
public GeneratedLightClassData createStubWithName(PsiJavaFileStub javaFileStub, BindingContext bindingContext) {
return new GeneratedLightClassData(javaFileStub, "", null);
}
@Override
public void generate(@NotNull GenerationState state, @NotNull Collection<JetFile> files) {
NamespaceCodegen codegen = state.getFactory().forNamespace(packageFqName, files);
codegen.generate(CompilationErrorHandler.THROW_EXCEPTION);
state.getFactory().asList();
}
});
@NotNull
@Override
public FqName getPackageFqName() {
return packageFqName;
}
@Override
public void generate(@NotNull GenerationState state, @NotNull Collection<JetFile> files) {
NamespaceCodegen codegen = state.getFactory().forNamespace(packageFqName, files);
codegen.generate(CompilationErrorHandler.THROW_EXCEPTION);
state.getFactory().asList();
}
}
);
}
@NotNull
public static KotlinJavaFileStubProvider createForDeclaredTopLevelClass(@NotNull final JetClassOrObject classOrObject) {
return new KotlinJavaFileStubProvider(classOrObject.getProject(), new StubGenerationStrategy.WithDeclaredClasses() {
private JetFile getFile() {
JetFile file = (JetFile) classOrObject.getContainingFile();
assert classOrObject.getParent() == file : "Not a top-level class: " + classOrObject.getText();
return file;
}
public static KotlinJavaFileStubProvider createForDeclaredClass(@NotNull final JetClassOrObject classOrObject) {
return new KotlinJavaFileStubProvider(
classOrObject.getProject(),
JetPsiUtil.isLocal(classOrObject),
new StubGenerationStrategy.WithDeclaredClasses() {
private JetFile getFile() {
return (JetFile) classOrObject.getContainingFile();
}
@NotNull
@Override
public Collection<JetFile> getFiles() {
return Collections.singletonList(getFile());
}
@NotNull
@Override
public LightClassConstructionContext createLightClassConstructionContext(@NotNull Collection<JetFile> files) {
return LightClassGenerationSupport.getInstance(classOrObject.getProject()).analyzeRelevantCode(classOrObject);
}
@NotNull
@Override
public FqName getPackageFqName() {
return JetPsiUtil.getFQName(getFile());
}
@NotNull
@Override
public GeneratedLightClassData createStubWithName(PsiJavaFileStub javaFileStub, BindingContext bindingContext) {
ClassDescriptor classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject);
assert (classDescriptor != null);
@Override
public void generate(@NotNull GenerationState state, @NotNull Collection<JetFile> files) {
NamespaceCodegen namespaceCodegen = state.getFactory().forNamespace(getPackageFqName(), files);
namespaceCodegen.generateClassOrObject(classOrObject);
state.getFactory().asList();
}
});
Type asmType = bindingContext.get(CodegenBinding.ASM_TYPE, classDescriptor);
assert (asmType != null);
String jvmInternalName = asmType.getClassName();
assert (jvmInternalName != null);
return new GeneratedLightClassData(javaFileStub, jvmInternalName, classDescriptor);
}
@NotNull
@Override
public Collection<JetFile> getFiles() {
return Collections.singletonList(getFile());
}
@NotNull
@Override
public FqName getPackageFqName() {
return JetPsiUtil.getFQName(getFile());
}
@Override
public void generate(@NotNull GenerationState state, @NotNull Collection<JetFile> files) {
NamespaceCodegen namespaceCodegen = state.getFactory().forNamespace(getPackageFqName(), files);
namespaceCodegen.generateClassOrObject(classOrObject);
state.getFactory().asList();
}
}
);
}
private static final Logger LOG = Logger.getInstance(KotlinJavaFileStubProvider.class);
private final Project project;
private final StubGenerationStrategy stubGenerationStrategy;
private final boolean local;
private KotlinJavaFileStubProvider(
@NotNull Project project,
boolean local,
@NotNull StubGenerationStrategy stubGenerationStrategy
) {
this.project = project;
this.stubGenerationStrategy = stubGenerationStrategy;
this.local = local;
}
@Nullable
@Override
public Result<PsiJavaFileStub> compute() {
public Result<GeneratedLightClassData> compute() {
FqName packageFqName = stubGenerationStrategy.getPackageFqName();
Collection<JetFile> files = stubGenerationStrategy.getFiles();
checkForBuiltIns(packageFqName, files);
LightClassConstructionContext context = LightClassGenerationSupport.getInstance(project).analyzeRelevantCode(files);
LightClassConstructionContext context = stubGenerationStrategy.createLightClassConstructionContext(files);
Throwable error = context.getError();
if (error != null) {
throw new IllegalStateException("failed to analyze: " + error, error);
}
PsiJavaFileStub javaFileStub = createJavaFileStub(packageFqName, getRepresentativeVirtualFile(files));
BindingContext bindingContext;
try {
Stack<StubElement> stubStack = new Stack<StubElement>();
stubStack.push(javaFileStub);
@@ -157,6 +202,8 @@ public class KotlinJavaFileStubProvider implements CachedValueProvider<PsiJavaFi
InlineUtil.DEFAULT_INLINE_FLAG_FOR_STUB);
state.beforeCompile();
bindingContext = state.getBindingContext();
stubGenerationStrategy.generate(state, files);
StubElement pop = stubStack.pop();
@@ -173,7 +220,10 @@ public class KotlinJavaFileStubProvider implements CachedValueProvider<PsiJavaFi
throw e;
}
return Result.create(javaFileStub, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
return Result.create(
stubGenerationStrategy.createStubWithName(javaFileStub, bindingContext),
local ? PsiModificationTracker.MODIFICATION_COUNT : PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
);
}
@NotNull
@@ -231,6 +281,9 @@ public class KotlinJavaFileStubProvider implements CachedValueProvider<PsiJavaFi
}
private interface StubGenerationStrategy {
@NotNull LightClassConstructionContext createLightClassConstructionContext(@NotNull Collection<JetFile> files);
@NotNull
GeneratedLightClassData createStubWithName(PsiJavaFileStub javaFileStub, BindingContext bindingContext);
@NotNull Collection<JetFile> getFiles();
@NotNull FqName getPackageFqName();
boolean generateDeclaredClasses();
@@ -42,7 +42,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.binding.PsiCodegenPredictor;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetJavaMirrorMarker;
import org.jetbrains.jet.lang.resolve.name.FqName;
@@ -59,7 +59,7 @@ import java.util.List;
import static org.jetbrains.jet.lexer.JetTokens.*;
public class KotlinLightClassForExplicitDeclaration extends KotlinWrappingLightClass implements KotlinLightClass, JetJavaMirrorMarker {
private final static Key<CachedValue<PsiJavaFileStub>> JAVA_API_STUB = Key.create("JAVA_API_STUB");
private final static Key<CachedValue<GeneratedLightClassData>> JAVA_API_STUB = Key.create("JAVA_API_STUB");
@Nullable
public static KotlinLightClassForExplicitDeclaration create(@NotNull PsiManager manager, @NotNull JetClassOrObject classOrObject) {
@@ -67,18 +67,21 @@ public class KotlinLightClassForExplicitDeclaration extends KotlinWrappingLightC
return null;
}
// TODO temporary not building light classes for local classes: e.g., they won't be visible in hierarchy
if (JetPsiUtil.getOutermostClassOrObject(classOrObject) == null) {
return null;
}
String jvmInternalName = PsiCodegenPredictor.getPredefinedJvmInternalName(classOrObject);
String jvmInternalName = getJvmInternalName(classOrObject);
if (jvmInternalName == null) return null;
FqName fqName = JvmClassName.byInternalName(jvmInternalName).getFqNameForClassNameWithoutDollars();
return new KotlinLightClassForExplicitDeclaration(manager, fqName, classOrObject);
}
private static String getJvmInternalName(JetClassOrObject classOrObject) {
if (JetPsiUtil.isLocal(classOrObject)) {
return getGeneratedLightClassData(classOrObject).getJvmInternalName();
}
return PsiCodegenPredictor.getPredefinedJvmInternalName(classOrObject);
}
private final FqName classFqName; // FqName of (possibly inner) class
private final JetClassOrObject classOrObject;
private PsiClass delegate;
@@ -153,12 +156,31 @@ public class KotlinLightClassForExplicitDeclaration extends KotlinWrappingLightC
@NotNull
private PsiJavaFileStub getJavaFileStub() {
return getGeneratedLightClassData().getJavaFileStub();
}
@NotNull
private ClassDescriptor getDescriptor() {
ClassDescriptor descriptor = getGeneratedLightClassData().getDescriptor();
assert descriptor != null;
return descriptor;
}
@NotNull
private GeneratedLightClassData getGeneratedLightClassData() {
return getGeneratedLightClassData(classOrObject);
}
@NotNull
private static GeneratedLightClassData getGeneratedLightClassData(JetClassOrObject classOrObject) {
JetClassOrObject outermostClassOrObject = getOutermostClassOrObject(classOrObject);
return CachedValuesManager.getManager(getProject()).getCachedValue(
return CachedValuesManager.getManager(classOrObject.getProject()).getCachedValue(
outermostClassOrObject,
JAVA_API_STUB,
KotlinJavaFileStubProvider.createForDeclaredTopLevelClass(outermostClassOrObject),
/*trackValue = */false);
KotlinJavaFileStubProvider.createForDeclaredClass(outermostClassOrObject),
/*trackValue = */false
);
}
@NotNull
@@ -392,6 +414,19 @@ public class KotlinLightClassForExplicitDeclaration extends KotlinWrappingLightC
return classOrObject.isValid();
}
@Override
public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) {
String qualifiedName;
if (baseClass instanceof KotlinLightClassForExplicitDeclaration) {
qualifiedName = DescriptorUtils.getFQName(((KotlinLightClassForExplicitDeclaration) baseClass).getDescriptor()).asString();
}
else {
qualifiedName = baseClass.getQualifiedName();
}
return ResolvePackage.checkSuperTypeByFQName(getDescriptor(), qualifiedName, checkDeep);
}
@Override
public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
throw new IncorrectOperationException("Cannot modify compiled kotlin element");
@@ -21,7 +21,6 @@ import com.intellij.navigation.ItemPresentation;
import com.intellij.navigation.ItemPresentationProviders;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub;
import com.intellij.psi.impl.light.LightEmptyImplementsList;
import com.intellij.psi.impl.light.LightModifierList;
import com.intellij.psi.javadoc.PsiDocComment;
@@ -32,8 +31,8 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetJavaMirrorMarker;
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetJavaMirrorMarker;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.plugin.JetLanguage;
@@ -48,7 +47,7 @@ public class KotlinLightClassForPackage extends KotlinWrappingLightClass impleme
private final GlobalSearchScope searchScope;
private final Collection<JetFile> files;
private final int hashCode;
private final CachedValue<PsiJavaFileStub> javaFileStub;
private final CachedValue<GeneratedLightClassData> javaFileStub;
private final PsiModifierList modifierList;
private final LightEmptyImplementsList implementsList;
@@ -266,7 +265,7 @@ public class KotlinLightClassForPackage extends KotlinWrappingLightClass impleme
@NotNull
@Override
public PsiClass getDelegate() {
PsiClass psiClass = LightClassUtil.findClass(packageClassFqName, javaFileStub.getValue());
PsiClass psiClass = LightClassUtil.findClass(packageClassFqName, javaFileStub.getValue().getJavaFileStub());
if (psiClass == null) {
throw new IllegalStateException("Package class was not found " + packageFqName);
}
@@ -38,6 +38,9 @@ public abstract class LightClassGenerationSupport {
@NotNull
public abstract LightClassConstructionContext analyzeRelevantCode(@NotNull Collection<JetFile> files);
@NotNull
public abstract LightClassConstructionContext analyzeRelevantCode(@NotNull JetClassOrObject classOrObject);
@NotNull
public abstract Collection<JetClassOrObject> findClassOrObjectDeclarations(@NotNull FqName fqName, @NotNull GlobalSearchScope searchScope);