New package part naming

This commit is contained in:
Michael Bogdanov
2015-06-23 14:08:50 +03:00
parent e388750ebb
commit 8e8ff860d6
11 changed files with 190 additions and 21 deletions
@@ -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<out String>) {
classInternalName = name
super.defineClass(origin, version, access, name, signature, superName, interfaces)
}
override fun done() {
if (classInternalName != null) {
handleClashingNames(classInternalName!!, classCreatedFor)
}
super.done()
}
}
}
@@ -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 ?
@@ -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<String, JvmDeclarationOrigin> ()
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))
}
}
@@ -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) {
@@ -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
@@ -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
@@ -69,6 +69,8 @@ public interface ErrorsJvm {
DiagnosticFactory2<JetElement, JetType, JetType> JAVA_CLASS_ON_COMPANION = DiagnosticFactory2.create(WARNING);
DiagnosticFactory2<JetExpression, JetType, JetType> JAVA_TYPE_MISMATCH = DiagnosticFactory2.create(ERROR);
DiagnosticFactory1<PsiElement, String> DUPLICATE_CLASS_NAMES = DiagnosticFactory1.create(ERROR);
enum NullabilityInformationSource {
KOTLIN {
@NotNull
@@ -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
@@ -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
);
}