From 8e8ff860d6ba6ae641d74c5e4f96ed3af764b292 Mon Sep 17 00:00:00 2001 From: Michael Bogdanov Date: Tue, 23 Jun 2015 14:08:50 +0300 Subject: [PATCH] New package part naming --- .../ClassNameCollectionClassBuilderFactory.kt | 72 +++++++++++++++++++ .../kotlin/codegen/inline/InlineCodegen.java | 13 ++-- ...FactoryForDuplicateClassNameDiagnostics.kt | 67 +++++++++++++++++ .../kotlin/codegen/state/GenerationState.kt | 3 + .../load/kotlin/PackagePartClassUtils.java | 43 +++++++---- .../diagnostics/DefaultErrorMessagesJvm.java | 3 + .../resolve/jvm/diagnostics/ErrorsJvm.java | 2 + .../{pseudocodeUtil.kt => pseudocodeUtils.kt} | 0 .../kotlin/codegen/CodegenTestCase.java | 5 +- .../KotlinSyntheticClassAnnotationTest.java | 3 +- ...lveTestUtil.kt => lazyResolveTestUtils.kt} | 0 11 files changed, 190 insertions(+), 21 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/ClassNameCollectionClassBuilderFactory.kt create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateClassNameDiagnostics.kt rename compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/{pseudocodeUtil.kt => pseudocodeUtils.kt} (100%) rename compiler/tests/org/jetbrains/kotlin/resolve/lazy/{lazyResolveTestUtil.kt => lazyResolveTestUtils.kt} (100%) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassNameCollectionClassBuilderFactory.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassNameCollectionClassBuilderFactory.kt new file mode 100644 index 00000000000..a2f9e72c3b3 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassNameCollectionClassBuilderFactory.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2015 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.kotlin.codegen + +import com.intellij.psi.PsiElement +import com.intellij.util.containers.MultiMap +import org.jetbrains.kotlin.resolve.jvm.diagnostics.ConflictingJvmDeclarationsData +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin +import org.jetbrains.kotlin.resolve.jvm.diagnostics.MemberKind +import org.jetbrains.kotlin.resolve.jvm.diagnostics.RawSignature +import org.jetbrains.org.objectweb.asm.FieldVisitor +import org.jetbrains.org.objectweb.asm.MethodVisitor + +public abstract class ClassNameCollectionClassBuilderFactory( + private val delegate: ClassBuilderFactory + +) : ClassBuilderFactory by delegate { + + protected abstract fun handleClashingNames(internalName: String, origin: JvmDeclarationOrigin) + + override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassNameCollectionClassBuilder { + return ClassNameCollectionClassBuilder(origin, delegate.newClassBuilder(origin)) + } + + public override fun asBytes(builder: ClassBuilder?): ByteArray? { + return delegate.asBytes((builder as ClassNameCollectionClassBuilder)._delegate) + } + + public override fun asText(builder: ClassBuilder?): String? { + return delegate.asText((builder as ClassNameCollectionClassBuilder)._delegate) + } + + public override fun close() { + delegate.close() + } + + private inner class ClassNameCollectionClassBuilder( + private val classCreatedFor: JvmDeclarationOrigin, + internal val _delegate: ClassBuilder + ) : DelegatingClassBuilder() { + + override fun getDelegate() = _delegate + + private var classInternalName: String? = null + + override fun defineClass(origin: PsiElement?, version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array) { + classInternalName = name + super.defineClass(origin, version, access, name, signature, superName, interfaces) + } + + override fun done() { + if (classInternalName != null) { + handleClashingNames(classInternalName!!, classCreatedFor) + } + super.done() + } + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java index a2886f8ad0d..6bda1edc7d1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java @@ -192,10 +192,10 @@ public class InlineCodegen extends CallGenerator { (DeserializedSimpleFunctionDescriptor) functionDescriptor); VirtualFile file = InlineCodegenUtil.getVirtualFileForCallable(containerClassId, state); - if (functionDescriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor) { - /*use facade class*/ - containerClassId = PackageClassUtils.getPackageClassId(containerClassId.getPackageFqName()); - } + //if (functionDescriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor) { + // /*use facade class*/ + // containerClassId = PackageClassUtils.getPackageClassId(containerClassId.getPackageFqName()); + //} nodeAndSMAP = InlineCodegenUtil.getMethodNode(file.contentsToByteArray(), asmMethod.getName(), asmMethod.getDescriptor(), @@ -226,7 +226,7 @@ public class InlineCodegen extends CallGenerator { SMAP smap; if (callDefault) { - Type ownerType = typeMapper.mapOwner(functionDescriptor, false/*use facade class*/); + Type ownerType = typeMapper.mapOwner(functionDescriptor, true/*TODO: false, migration*/); FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen(), inliningFunction, (FieldOwnerContext) methodContext.getParentContext(), ownerType.getInternalName()); @@ -337,7 +337,8 @@ public class InlineCodegen extends CallGenerator { FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen(), expression, (FieldOwnerContext) context.getParentContext(), - isLambda ? codegen.getParentCodegen().getClassName() : typeMapper.mapOwner(descriptor, false).getInternalName()); + isLambda ? codegen.getParentCodegen().getClassName() + : typeMapper.mapOwner(descriptor, true /*TODO: false, migration*/).getInternalName()); FunctionGenerationStrategy strategy = expression instanceof JetCallableReferenceExpression ? diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateClassNameDiagnostics.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateClassNameDiagnostics.kt new file mode 100644 index 00000000000..a5571785d9f --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateClassNameDiagnostics.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2010-2015 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.kotlin.codegen.state + +import com.intellij.psi.PsiElement +import com.intellij.util.containers.MultiMap +import org.jetbrains.kotlin.codegen.ClassBuilderFactory +import org.jetbrains.kotlin.codegen.ClassBuilderMode +import org.jetbrains.kotlin.codegen.ClassNameCollectionClassBuilderFactory +import org.jetbrains.kotlin.codegen.SignatureCollectingClassBuilderFactory +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.DiagnosticSink +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.load.java.descriptors.SamAdapterDescriptor +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.jvm +import org.jetbrains.kotlin.resolve.jvm.diagnostics.* +import org.jetbrains.kotlin.utils.addIfNotNull +import java.util.* + + +class BuilderFactoryForDuplicateClassNameDiagnostics( + builderFactory: ClassBuilderFactory, + private val diagnostics: DiagnosticSink +) : ClassNameCollectionClassBuilderFactory(builderFactory) { + + private val className = hashMapOf () + + override fun handleClashingNames(internalName: String, origin: JvmDeclarationOrigin) { + val another = className.getOrPut(internalName, { origin }) + if (origin.element != another.element) { + if (origin.originKind == JvmDeclarationOriginKind.PACKAGE_FACADE || another.originKind == JvmDeclarationOriginKind.PACKAGE_FACADE) { + if (origin.originKind == JvmDeclarationOriginKind.PACKAGE_FACADE) { + reportError(another, internalName) + } else { + reportError(another, internalName) + } + } else { + if (origin.element != null) { + reportError(origin, internalName) + } + if (another.element != null) { + reportError(another, internalName) + } + } + } + } + + private fun reportError(another: JvmDeclarationOrigin, internalName: String) { + diagnostics.report(ErrorsJvm.DUPLICATE_CLASS_NAMES.on(another.element, internalName)) + } +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index 932c5b9523d..5ed20276403 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -102,6 +102,9 @@ public class GenerationState jvmOverloads constructor( val optimizationClassBuilderFactory = OptimizationClassBuilderFactory(builderFactory, disableOptimization) var interceptedBuilderFactory: ClassBuilderFactory = BuilderFactoryForDuplicateSignatureDiagnostics( optimizationClassBuilderFactory, this.bindingContext, diagnostics) + + interceptedBuilderFactory = BuilderFactoryForDuplicateClassNameDiagnostics(interceptedBuilderFactory, diagnostics); + val interceptExtensions = ClassBuilderInterceptorExtension.getInstances(project) for (extension in interceptExtensions) { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.java b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.java index 6c027bcf2b3..4dd6a8baf57 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.java @@ -19,16 +19,16 @@ package org.jetbrains.kotlin.load.kotlin; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; import com.intellij.util.PathUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.Name; -import org.jetbrains.kotlin.psi.JetDeclaration; -import org.jetbrains.kotlin.psi.JetFile; -import org.jetbrains.kotlin.psi.JetNamedFunction; -import org.jetbrains.kotlin.psi.JetProperty; +import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.jvm.JvmClassName; import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf; @@ -50,18 +50,35 @@ public class PackagePartClassUtils { } @NotNull + @TestOnly public static FqName getPackagePartFqName(@NotNull FqName facadeFqName, @NotNull VirtualFile file) { + return getPackagePartFqName(facadeFqName, file, null); + } + + @NotNull + public static FqName getPackagePartFqName(@NotNull FqName facadeFqName, @NotNull VirtualFile file, @Nullable JetFile jetFile) { String fileName = FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.getName())); - // path hashCode to prevent same name / different path collision - String srcName = String.format( - "%s$%s$%08x", - facadeFqName.shortName().asString(), - replaceSpecialSymbols(fileName), - getPathHashCode(file) - ); + if (!fileName.isEmpty()) { + char c = fileName.charAt(0); + //!Character.isUpperCase also handles non-latin characters + if ('a' <= c && c <= 'z') { + fileName = Character.toUpperCase(fileName.charAt(0)) + fileName.substring(1); + } + } - return facadeFqName.parent().child(Name.identifier(srcName)); + if (jetFile != null) { + for (PsiElement child : jetFile.getDeclarations()) { + if (child instanceof JetClassOrObject) { + if (fileName.equalsIgnoreCase(((JetClassOrObject) child).getName())) { + fileName += "_"; + break; + } + } + } + } + + return facadeFqName.parent().child(Name.identifier(replaceSpecialSymbols(fileName))); } @NotNull @@ -77,7 +94,7 @@ public class PackagePartClassUtils { @NotNull public static FqName getPackagePartFqName(@NotNull JetFile file) { - return getPackagePartFqName(getPackageClassFqName(file.getPackageFqName()), file.getVirtualFile()); + return getPackagePartFqName(getPackageClassFqName(file.getPackageFqName()), file.getVirtualFile(), file); } @NotNull diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java index c4d53c63757..71818e06d1c 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java @@ -81,8 +81,11 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { "Please use the more clear ''::class.java'' syntax to avoid confusion", Renderers.RENDER_TYPE, Renderers.RENDER_TYPE ); + MAP.put(ErrorsJvm.JAVA_TYPE_MISMATCH, "Java type mismatch expected {1} but found {0}. Use explicit cast", Renderers.RENDER_TYPE, Renderers.RENDER_TYPE); + + MAP.put(ErrorsJvm.DUPLICATE_CLASS_NAMES, "Class names ''{0}'' conflict with package name (facade or part)", Renderers.TO_STRING); } @NotNull diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java index 59f049a63ed..e1c0c5c1491 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java @@ -69,6 +69,8 @@ public interface ErrorsJvm { DiagnosticFactory2 JAVA_CLASS_ON_COMPANION = DiagnosticFactory2.create(WARNING); DiagnosticFactory2 JAVA_TYPE_MISMATCH = DiagnosticFactory2.create(ERROR); + DiagnosticFactory1 DUPLICATE_CLASS_NAMES = DiagnosticFactory1.create(ERROR); + enum NullabilityInformationSource { KOTLIN { @NotNull diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt similarity index 100% rename from compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtil.kt rename to compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java b/compiler/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java index d455b4e9c66..7d2b4c0150d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java @@ -53,6 +53,9 @@ import static org.jetbrains.kotlin.codegen.CodegenTestUtil.*; import static org.jetbrains.kotlin.load.kotlin.PackageClassUtils.getPackageClassFqName; public abstract class CodegenTestCase extends UsefulTestCase { + + public static final String DEFAULT_TEST_FILE_NAME = "a_test"; + protected KotlinCoreEnvironment myEnvironment; protected CodegenTestFiles myFiles; protected ClassFileFactory classFileFactory; @@ -81,7 +84,7 @@ public abstract class CodegenTestCase extends UsefulTestCase { } protected void loadText(@NotNull String text) { - myFiles = CodegenTestFiles.create("a.kt", text, myEnvironment.getProject()); + myFiles = CodegenTestFiles.create(DEFAULT_TEST_FILE_NAME + ".kt", text, myEnvironment.getProject()); } @NotNull diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/KotlinSyntheticClassAnnotationTest.java b/compiler/tests/org/jetbrains/kotlin/codegen/KotlinSyntheticClassAnnotationTest.java index bc7ce29e0f7..796b669ef1b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/KotlinSyntheticClassAnnotationTest.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/KotlinSyntheticClassAnnotationTest.java @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.codegen; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; +import kotlin.KotlinPackage; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.backend.common.output.OutputFile; import org.jetbrains.kotlin.load.java.AbiVersionUtil; @@ -50,7 +51,7 @@ public class KotlinSyntheticClassAnnotationTest extends CodegenTestCase { public void testPackagePart() { doTestKotlinSyntheticClass( "fun foo() = 42", - "$", + KotlinPackage.capitalize(DEFAULT_TEST_FILE_NAME), PACKAGE_PART ); } diff --git a/compiler/tests/org/jetbrains/kotlin/resolve/lazy/lazyResolveTestUtil.kt b/compiler/tests/org/jetbrains/kotlin/resolve/lazy/lazyResolveTestUtils.kt similarity index 100% rename from compiler/tests/org/jetbrains/kotlin/resolve/lazy/lazyResolveTestUtil.kt rename to compiler/tests/org/jetbrains/kotlin/resolve/lazy/lazyResolveTestUtils.kt