From 5d4e4707ca974e96a7f6990f8e0caa98a43e487f Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 20 Dec 2012 19:11:34 +0400 Subject: [PATCH] Prototype implementation of KotlinLightClassForPackage --- .../asJava/KotlinLightClassForPackage.java | 169 ++++++++++++++++ .../KotlinLightClassForPackageBase.java | 190 ++++++++++++++++++ .../KotlinLightClassForPackageProvider.java | 145 +++++++++++++ 3 files changed, 504 insertions(+) create mode 100644 compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackage.java create mode 100644 compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackageBase.java create mode 100644 compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackageProvider.java 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 new file mode 100644 index 00000000000..4bdabe4036f --- /dev/null +++ b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackage.java @@ -0,0 +1,169 @@ +/* + * 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.Sets; +import com.intellij.navigation.ItemPresentation; +import com.intellij.navigation.ItemPresentationProviders; +import com.intellij.openapi.util.Comparing; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiManager; +import com.intellij.psi.util.CachedValue; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.util.IncorrectOperationException; +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.psi.JetNamespaceHeader; +import org.jetbrains.jet.lang.psi.JetPsiFactory; +import org.jetbrains.jet.lang.resolve.java.JetJavaMirrorMarker; +import org.jetbrains.jet.lang.resolve.name.FqName; +import org.jetbrains.jet.lang.resolve.name.Name; + +import javax.swing.*; +import java.util.Collection; + +public class KotlinLightClassForPackage extends KotlinLightClassForPackageBase implements JetJavaMirrorMarker { + + private final FqName fqName; + private final Collection files; + private final int hashCode; + private CachedValue delegate; + + public KotlinLightClassForPackage(@NotNull PsiManager manager, @NotNull FqName fqName, @NotNull Collection files) { + super(manager); + this.fqName = fqName; + 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); + this.delegate = CachedValuesManager.getManager(getProject()).createCachedValue(stubProvider); + } + + @Nullable + @Override + public String getName() { + return fqName.shortName().getName(); + } + + @Nullable + @Override + public String getQualifiedName() { + return fqName.getFqName(); + } + + @Override + public boolean isValid() { + return allValid(files); + } + + private static boolean allValid(Collection files) { + for (JetFile file : files) { + if (!file.isValid()) return false; + } + return true; + } + + @NotNull + @Override + public PsiElement copy() { + return new KotlinLightClassForPackage(getManager(), fqName, files); + } + + @NotNull + @Override + public PsiClass getDelegate() { + return delegate.getValue(); + } + + @NotNull + @Override + public PsiElement getNavigationElement() { + return files.iterator().next(); + } + + @Override + public boolean isEquivalentTo(PsiElement another) { + return another instanceof PsiClass && Comparing.equal(((PsiClass) another).getQualifiedName(), getQualifiedName()); + } + + @Override + public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException { + for (JetFile file : files) { + JetNamespaceHeader header = file.getNamespaceHeader(); + assert header != null : "Cannot rename a package of a script"; + String newHeaderText = "package " + fqName.parent().child(Name.identifier(name)).toString(); + JetNamespaceHeader newHeader = JetPsiFactory.createFile(getProject(), newHeaderText).getNamespaceHeader(); + assert newHeader != null; + header.replace(newHeader); + } + // TODO: some other files may now belong to the same package + return this; + } + + @Override + public ItemPresentation getPresentation() { + return ItemPresentationProviders.getItemPresentation(this); + } + + @Override + public Icon getElementIcon(final int flags) { + throw new UnsupportedOperationException("This should be done byt JetIconProvider"); + } + + @Override + public int hashCode() { + return hashCode; + } + + private int computeHashCode() { + int result = getManager().hashCode(); + result = 31 * result + files.hashCode(); + result = 31 * result + fqName.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + KotlinLightClassForPackage lightClass = (KotlinLightClassForPackage) obj; + + if (this.hashCode != lightClass.hashCode) return false; + if (getManager() != lightClass.getManager()) return false; + if (!files.equals(lightClass.files)) return false; + if (!fqName.equals(lightClass.fqName)) return false; + + return true; + } + + @Override + public String toString() { + try { + return KotlinLightClassForPackage.class.getSimpleName() + ":" + getQualifiedName(); + } + catch (Throwable e) { + return KotlinLightClassForPackage.class.getSimpleName() + ":" + e.toString(); + } + } + +} diff --git a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackageBase.java b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackageBase.java new file mode 100644 index 00000000000..ca5481a8b8e --- /dev/null +++ b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackageBase.java @@ -0,0 +1,190 @@ +/* + * 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.*; +import com.intellij.psi.impl.light.AbstractLightClass; +import com.intellij.psi.impl.light.LightEmptyImplementsList; +import com.intellij.psi.impl.light.LightModifierList; +import com.intellij.psi.javadoc.PsiDocComment; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.plugin.JetLanguage; + +/** + * This class contains method that are straightforward, and only would pollute the actual implementation + */ +public abstract class KotlinLightClassForPackageBase extends AbstractLightClass { + + private final PsiModifierList modifierList; + private final LightEmptyImplementsList implementsList; + + public KotlinLightClassForPackageBase(PsiManager manager) { + super(manager, JetLanguage.INSTANCE); + this.modifierList = new LightModifierList(manager, JetLanguage.INSTANCE, PsiModifier.PUBLIC, PsiModifier.FINAL); + this.implementsList = new LightEmptyImplementsList(manager); + } + + @Nullable + @Override + public PsiModifierList getModifierList() { + return modifierList; + } + + @Override + public boolean hasModifierProperty(@NonNls @NotNull String name) { + return modifierList.hasModifierProperty(name); + } + + @Override + public boolean isDeprecated() { + return false; + } + + @Override + public boolean isInterface() { + return false; + } + + @Override + public boolean isAnnotationType() { + return false; + } + + @Override + public boolean isEnum() { + return false; + } + + @Nullable + @Override + public PsiClass getContainingClass() { + return null; + } + + @Override + public boolean hasTypeParameters() { + return false; + } + + @NotNull + @Override + public PsiTypeParameter[] getTypeParameters() { + return PsiTypeParameter.EMPTY_ARRAY; + } + + @Nullable + @Override + public PsiTypeParameterList getTypeParameterList() { + return null; + } + + @Nullable + @Override + public PsiDocComment getDocComment() { + return null; + } + + @Nullable + @Override + public PsiReferenceList getImplementsList() { + return implementsList; + } + + @NotNull + @Override + public PsiClassType[] getImplementsListTypes() { + return PsiClassType.EMPTY_ARRAY; + } + + @Nullable + @Override + public PsiReferenceList getExtendsList() { + // TODO: Find a way to return just Object + return super.getExtendsList(); + } + + @NotNull + @Override + public PsiClassType[] getExtendsListTypes() { + // TODO see getExtendsList() + return super.getExtendsListTypes(); + } + + @Nullable + @Override + public PsiClass getSuperClass() { + // TODO see getExtendsList() + return super.getSuperClass(); + } + + @NotNull + @Override + public PsiClass[] getSupers() { + // TODO see getExtendsList() + return super.getSupers(); + } + + @NotNull + @Override + public PsiClassType[] getSuperTypes() { + // TODO see getExtendsList() + return super.getSuperTypes(); + } + + @Override + public PsiClass[] getInterfaces() { + return PsiClass.EMPTY_ARRAY; + } + + @NotNull + @Override + public PsiClass[] getInnerClasses() { + return PsiClass.EMPTY_ARRAY; + } + + @NotNull + @Override + public PsiClassInitializer[] getInitializers() { + return PsiClassInitializer.EMPTY_ARRAY; + } + + @NotNull + @Override + public PsiClass[] getAllInnerClasses() { + return PsiClass.EMPTY_ARRAY; + } + + @Nullable + @Override + public PsiClass findInnerClassByName(@NonNls String name, boolean checkBases) { + return null; + } + + @Nullable + @Override + public PsiElement getLBrace() { + return null; + } + + @Nullable + @Override + public PsiElement getRBrace() { + return null; + } +} 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 new file mode 100644 index 00000000000..cae646aaba8 --- /dev/null +++ b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/KotlinLightClassForPackageProvider.java @@ -0,0 +1,145 @@ +/* + * 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.apacheither 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.ClassBuilder; +import org.jetbrains.jet.codegen.ClassBuilderFactory; +import org.jetbrains.jet.codegen.ClassBuilderMode; +import org.jetbrains.jet.codegen.CompilationErrorHandler; +import org.jetbrains.jet.codegen.state.GenerationState; +import org.jetbrains.jet.codegen.state.GenerationStrategy; +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(); + + final PsiJavaFileStubImpl javaFileStub = new PsiJavaFileStubImpl(fqName.getFqName(), true); + + final Stack stubStack = new Stack(); + + final ClassBuilderFactory builderFactory = new KotlinLightClassBuilderFactory(stubStack); + + // 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 + LightClassConstructionContext context = LightClassGenerationSupport.getInstance(project).analyzeRelevantCode(files); + + Throwable error = context.getError(); + if (error != null) { + throw new IllegalStateException("failed to analyze: " + error, error); + } + + try { + GenerationState state = new GenerationState(project, builderFactory, context.getBindingContext(), Lists.newArrayList(files)); + GenerationStrategy strategy = new LightClassGenerationStrategy(new LightVirtualFile(), stubStack, javaFileStub); + + strategy.compileCorrectFiles(state, 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()); + } + } + } + + private static class KotlinLightClassBuilderFactory implements ClassBuilderFactory { + private final Stack stubStack; + + public KotlinLightClassBuilderFactory(Stack stubStack) { + this.stubStack = stubStack; + } + + @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 + } + } +} \ No newline at end of file