diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/NamespaceCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/NamespaceCodegen.java index 3afb65e43d1..1fedb364ac6 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/NamespaceCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/NamespaceCodegen.java @@ -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 diff --git a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClass.java b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClass.java new file mode 100644 index 00000000000..3d02345cd56 --- /dev/null +++ b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClass.java @@ -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 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); + } +} diff --git a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackage.java b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackage.java index e9e7ea10530..17a4f98ac10 100644 --- a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackage.java +++ b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackage.java @@ -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); } diff --git a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackageProvider.java b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackageProvider.java deleted file mode 100644 index 7ba8103cf9b..00000000000 --- a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackageProvider.java +++ /dev/null @@ -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 { - private final Collection files; - private final FqName fqName; - private final Project project; - - public KotlinLightClassForPackageProvider(@NotNull Project project, @NotNull FqName fqName, @NotNull Collection files) { - this.files = files; - this.fqName = fqName; - this.project = project; - } - - @Nullable - @Override - public Result 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 stubStack = new Stack(); - 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 dependencies = Lists.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()); - } - } - } -} \ No newline at end of file diff --git a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassProvider.java b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassProvider.java new file mode 100644 index 00000000000..338d9816e30 --- /dev/null +++ b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassProvider.java @@ -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 { + + public static KotlinLightClassProvider createForPackageClass( + @NotNull Project project, + @NotNull FqName packageFqName, + @NotNull Collection 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 files; + private final FqName fqName; + private final Project project; + private final StubGenerationStrategy stubGenerationStrategy; + + private KotlinLightClassProvider( + @NotNull Project project, + @NotNull FqName fqName, + @NotNull Collection files, + @NotNull StubGenerationStrategy stubGenerationStrategy + ) { + this.files = files; + this.fqName = fqName; + this.project = project; + this.stubGenerationStrategy = stubGenerationStrategy; + } + + @Nullable + @Override + public Result 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 stubStack = new Stack(); + 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 dependencies = Lists.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 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 ? "" : 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; + } + } + } +} \ No newline at end of file