Light class providers unified

This commit is contained in:
Andrey Breslav
2012-12-25 14:14:36 +04:00
parent bf4fbe4aff
commit a577ccabab
5 changed files with 276 additions and 124 deletions
@@ -125,8 +125,7 @@ public class NamespaceCodegen extends MemberCodegen {
}
else if (declaration instanceof JetClassOrObject) {
if (state.isGenerateDeclaredClasses()) {
final CodegenContext context = CodegenContext.STATIC.intoNamespace(descriptor);
genClassOrObject(context, (JetClassOrObject) declaration);
generateClassOrObject(descriptor, (JetClassOrObject) declaration);
}
}
else if (declaration instanceof JetScript) {
@@ -177,6 +176,11 @@ public class NamespaceCodegen extends MemberCodegen {
}
}
public void generateClassOrObject(@NotNull NamespaceDescriptor descriptor, @NotNull JetClassOrObject classOrObject) {
CodegenContext context = CodegenContext.STATIC.intoNamespace(descriptor);
genClassOrObject(context, classOrObject);
}
/**
* @param namespaceFiles all files should have same package name
* @return
@@ -0,0 +1,56 @@
/*
* 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.
*/
package org.jetbrains.jet.asJava;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.light.AbstractLightClass;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValuesManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.jet.lang.resolve.java.JetJavaMirrorMarker;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.plugin.JetLanguage;
public class KotlinLightClass extends AbstractLightClass implements JetJavaMirrorMarker {
private final FqName fqName;
private final JetClassOrObject jetClassOrObject;
private final CachedValue<PsiClass> delegate;
protected KotlinLightClass(@NotNull PsiManager manager, @NotNull FqName name, @NotNull JetClassOrObject object) {
super(manager, JetLanguage.INSTANCE);
this.fqName = name;
this.jetClassOrObject = object;
KotlinLightClassProvider stubProvider = KotlinLightClassProvider.createForDeclaredClass(getProject(), fqName, jetClassOrObject);
this.delegate = CachedValuesManager.getManager(getProject()).createCachedValue(stubProvider);
}
@NotNull
@Override
public PsiClass getDelegate() {
return delegate.getValue();
}
@NotNull
@Override
public PsiElement copy() {
return new KotlinLightClass(getManager(), fqName, jetClassOrObject);
}
}
@@ -52,7 +52,7 @@ public class KotlinLightClassForPackage extends KotlinLightClassForPackageBase i
assert !files.isEmpty() : "No files for package " + fqName;
this.files = Sets.newHashSet(files); // needed for hashCode
this.hashCode = computeHashCode();
KotlinLightClassForPackageProvider stubProvider = new KotlinLightClassForPackageProvider(manager.getProject(), fqName, files);
KotlinLightClassProvider stubProvider = KotlinLightClassProvider.createForPackageClass(getProject(), fqName, files);
this.delegate = CachedValuesManager.getManager(getProject()).createCachedValue(stubProvider);
}
@@ -1,121 +0,0 @@
/*
* 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.
*/
package org.jetbrains.jet.asJava;
import com.google.common.collect.Lists;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.PsiClass;
import com.intellij.psi.impl.java.stubs.PsiClassStub;
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.*;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.GenerationStrategy;
import org.jetbrains.jet.codegen.state.Progress;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import java.util.Collection;
import java.util.List;
public class KotlinLightClassForPackageProvider implements CachedValueProvider<PsiClass> {
private final Collection<JetFile> files;
private final FqName fqName;
private final Project project;
public KotlinLightClassForPackageProvider(@NotNull Project project, @NotNull FqName fqName, @NotNull Collection<JetFile> files) {
this.files = files;
this.fqName = fqName;
this.project = project;
}
@Nullable
@Override
public Result<PsiClass> compute() {
checkForBuiltIns();
LightClassConstructionContext context = LightClassGenerationSupport.getInstance(project).analyzeRelevantCode(files);
Throwable error = context.getError();
if (error != null) {
throw new IllegalStateException("failed to analyze: " + error, error);
}
PsiJavaFileStubImpl javaFileStub = new PsiJavaFileStubImpl(fqName.getFqName(), true);
try {
Stack<StubElement> stubStack = new Stack<StubElement>();
ClassBuilderFactory builderFactory = new KotlinLightClassBuilderFactory(stubStack);
GenerationState state = new GenerationState(
project,
builderFactory,
Progress.DEAF,
context.getBindingContext(),
Lists.newArrayList(files),
BuiltinToJavaTypesMapping.ENABLED,
/*not-null assertions*/false, false,
/*generateDeclaredClasses=*/false);
GenerationStrategy strategy = new LightClassGenerationStrategy(new LightVirtualFile(), stubStack, javaFileStub);
KotlinCodegenFacade.compileCorrectFiles(state, strategy, CompilationErrorHandler.THROW_EXCEPTION);
state.getFactory().files();
}
catch (ProcessCanceledException e) {
throw e;
}
catch (RuntimeException e) {
LightClassUtil.logErrorWithOSInfo(e, fqName, null);
throw e;
}
FqName packageClassFqName = fqName.child(Name.identifier(JvmAbi.PACKAGE_CLASS));
for (StubElement child : javaFileStub.getChildrenStubs()) {
if (child instanceof PsiClassStub && Comparing.equal(packageClassFqName.getFqName(), ((PsiClassStub) child).getQualifiedName())) {
PsiClass result = (PsiClass)child.getPsi();
List<Object> dependencies = Lists.<Object>newArrayList(files);
dependencies.add(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
return Result.create(result, files);
}
}
throw new IllegalStateException("Namespace class was not found " + packageClassFqName + " for files " + files);
}
private void checkForBuiltIns() {
for (JetFile file : files) {
if (LightClassUtil.belongsToKotlinBuiltIns(file)) {
// We may not fail later due to some luck, but generating JetLightClasses for built-ins is a bad idea anyways
// If it fails later, there will be an exception logged
LightClassUtil.logErrorWithOSInfo(null, fqName, file.getVirtualFile());
}
}
}
}
@@ -0,0 +1,213 @@
/*
* 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.
*/
package org.jetbrains.jet.asJava;
import com.google.common.collect.Lists;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.impl.java.stubs.PsiClassStub;
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl;
import com.intellij.psi.stubs.PsiFileStub;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.*;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.GenerationStrategy;
import org.jetbrains.jet.codegen.state.Progress;
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class KotlinLightClassProvider implements CachedValueProvider<PsiClass> {
public static KotlinLightClassProvider createForPackageClass(
@NotNull Project project,
@NotNull FqName packageFqName,
@NotNull Collection<JetFile> files
) {
FqName packageClassFqName = packageFqName.child(Name.identifier(JvmAbi.PACKAGE_CLASS));
return new KotlinLightClassProvider(project, packageClassFqName, files, new StubGenerationStrategy.NoDeclaredClasses() {
@Override
public void generate(GenerationState state, GenerationStrategy strategy) {
KotlinCodegenFacade.compileCorrectFiles(state, strategy, CompilationErrorHandler.THROW_EXCEPTION);
state.getFactory().files();
}
});
}
public static KotlinLightClassProvider createForDeclaredClass(
@NotNull Project project,
@NotNull final FqName classFqName,
@NotNull final JetClassOrObject jetClassOrObject
) {
JetFile file = (JetFile) jetClassOrObject.getContainingFile();
return new KotlinLightClassProvider(project, classFqName, Collections.singletonList(file), new StubGenerationStrategy.WithDeclaredClasses() {
@Override
public void generate(GenerationState state, GenerationStrategy strategy) {
FqName packageFqName = classFqName.parent();
NamespaceCodegen namespaceCodegen = state.getFactory().forNamespace(packageFqName, state.getFiles());
NamespaceDescriptor namespaceDescriptor =
state.getBindingContext().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, packageFqName);
assert namespaceDescriptor != null : "No package descriptor for " + packageFqName + " for class " + jetClassOrObject.getText();
namespaceCodegen.generateClassOrObject(namespaceDescriptor, jetClassOrObject);
state.getFactory().files();
}
});
}
private static final Logger LOG = Logger.getInstance(KotlinLightClassProvider.class);
private final Collection<JetFile> files;
private final FqName fqName;
private final Project project;
private final StubGenerationStrategy stubGenerationStrategy;
private KotlinLightClassProvider(
@NotNull Project project,
@NotNull FqName fqName,
@NotNull Collection<JetFile> files,
@NotNull StubGenerationStrategy stubGenerationStrategy
) {
this.files = files;
this.fqName = fqName;
this.project = project;
this.stubGenerationStrategy = stubGenerationStrategy;
}
@Nullable
@Override
public Result<PsiClass> compute() {
checkForBuiltIns(fqName, files);
LightClassConstructionContext context = LightClassGenerationSupport.getInstance(project).analyzeRelevantCode(files);
Throwable error = context.getError();
if (error != null) {
throw new IllegalStateException("failed to analyze: " + error, error);
}
PsiJavaFileStubImpl javaFileStub = new PsiJavaFileStubImpl(fqName.parent().getFqName(), true);
try {
Stack<StubElement> stubStack = new Stack<StubElement>();
ClassBuilderFactory builderFactory = new KotlinLightClassBuilderFactory(stubStack);
GenerationState state = new GenerationState(
project,
builderFactory,
Progress.DEAF,
context.getBindingContext(),
Lists.newArrayList(files),
BuiltinToJavaTypesMapping.ENABLED,
/*not-null assertions*/false, false,
/*generateDeclaredClasses=*/stubGenerationStrategy.generateDeclaredClasses());
GenerationStrategy strategy = new LightClassGenerationStrategy(new LightVirtualFile(), stubStack, javaFileStub);
stubGenerationStrategy.generate(state, strategy);
}
catch (ProcessCanceledException e) {
throw e;
}
catch (RuntimeException e) {
logErrorWithOSInfo(e, fqName, null);
throw e;
}
PsiClass psiClass = findClass(fqName, javaFileStub);
if (psiClass == null) {
throw new IllegalStateException("Class was not found " + fqName + " for files " + files);
}
List<Object> dependencies = Lists.<Object>newArrayList(files);
dependencies.add(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
return Result.create(psiClass, files);
}
private static void checkForBuiltIns(@NotNull FqName classFqName, @NotNull Collection<JetFile> files) {
for (JetFile file : files) {
if (LightClassUtil.belongsToKotlinBuiltIns(file)) {
// We may not fail later due to some luck, but generating JetLightClasses for built-ins is a bad idea anyways
// If it fails later, there will be an exception logged
logErrorWithOSInfo(null, classFqName, file.getVirtualFile());
}
}
}
private static void logErrorWithOSInfo(@Nullable Throwable cause, @NotNull FqName fqName, @Nullable VirtualFile virtualFile) {
String path = virtualFile == null ? "<null>" : virtualFile.getPath();
LOG.error(
"Could not generate LightClass for " + fqName + " declared in " + path + "\n" +
"built-ins dir URL is " + LightClassUtil.getBuiltInsDirResourceUrl() + "\n" +
"System: " + SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION + " Java Runtime: " + SystemInfo.JAVA_RUNTIME_VERSION,
cause);
}
@Nullable
private static PsiClass findClass(@NotNull FqName fqn, @NotNull StubElement<?> stub) {
if (stub instanceof PsiClassStub && Comparing.equal(fqn.getFqName(), ((PsiClassStub) stub).getQualifiedName())) {
return (PsiClass)stub.getPsi();
}
if (stub instanceof PsiClassStub || stub instanceof PsiFileStub) {
for (StubElement child : stub.getChildrenStubs()) {
PsiClass answer = findClass(fqn, child);
if (answer != null) return answer;
}
}
return null;
}
private interface StubGenerationStrategy {
boolean generateDeclaredClasses();
void generate(GenerationState state, GenerationStrategy strategy);
abstract class NoDeclaredClasses implements StubGenerationStrategy {
@Override
public boolean generateDeclaredClasses() {
return false;
}
}
abstract class WithDeclaredClasses implements StubGenerationStrategy {
@Override
public boolean generateDeclaredClasses() {
return true;
}
}
}
}