Generate delegates to multifile class members in package facade classes

(unless using -Xmultifile-package-facades option)
This commit is contained in:
Dmitry Petrov
2015-09-22 16:34:53 +03:00
parent 5cdbdfc2cc
commit 3cd1c222f0
13 changed files with 90 additions and 37 deletions
@@ -63,32 +63,37 @@ public class KotlinCodegenFacade {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
MultiMap<FqName, JetFile> packageFqNameToFiles = new MultiMap<FqName, JetFile>(); MultiMap<FqName, JetFile> filesInPackageClasses = new MultiMap<FqName, JetFile>();
MultiMap<FqName, JetFile> multifileClassFqNameToFiles = new MultiMap<FqName, JetFile>(); MultiMap<FqName, JetFile> filesInMultifileClasses = new MultiMap<FqName, JetFile>();
for (JetFile file : state.getFiles()) { for (JetFile file : state.getFiles()) {
if (file == null) throw new IllegalArgumentException("A null file given for compilation"); if (file == null) throw new IllegalArgumentException("A null file given for compilation");
JvmFileClassInfo fileClassInfo = state.getFileClassesProvider().getFileClassInfo(file); JvmFileClassInfo fileClassInfo = state.getFileClassesProvider().getFileClassInfo(file);
if (fileClassInfo.getIsMultifileClass()) { if (fileClassInfo.getIsMultifileClass()) {
multifileClassFqNameToFiles.putValue(fileClassInfo.getFacadeClassFqName(), file); filesInMultifileClasses.putValue(fileClassInfo.getFacadeClassFqName(), file);
} }
else if (state.getPackageFacadesAsMultifileClasses()) {
multifileClassFqNameToFiles.putValue(PackageClassUtils.getPackageClassFqName(file.getPackageFqName()), file); if (state.getPackageFacadesAsMultifileClasses()) {
if (!fileClassInfo.getIsMultifileClass()) {
filesInMultifileClasses.putValue(PackageClassUtils.getPackageClassFqName(file.getPackageFqName()), file);
}
} }
else { else {
packageFqNameToFiles.putValue(file.getPackageFqName(), file); filesInPackageClasses.putValue(file.getPackageFqName(), file);
} }
} }
Set<FqName> packagesWithObsoleteParts = new HashSet<FqName>(state.getPackagesWithObsoleteParts()); Set<FqName> packagesWithObsoleteParts = new HashSet<FqName>(state.getPackagesWithObsoleteParts());
for (FqName packageFqName : Sets.union(packagesWithObsoleteParts, packageFqNameToFiles.keySet())) { for (FqName packageFqName : Sets.union(packagesWithObsoleteParts, filesInPackageClasses.keySet())) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
generatePackage(state, packageFqName, packageFqNameToFiles.get(packageFqName), errorHandler); generatePackage(state, packageFqName, filesInPackageClasses.get(packageFqName), errorHandler);
} }
for (FqName multifileClassFqName : multifileClassFqNameToFiles.keySet()) { for (FqName multifileClassFqName : filesInMultifileClasses.keySet()) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
generateMultifileClass(state, multifileClassFqName, multifileClassFqNameToFiles.get(multifileClassFqName), errorHandler); generateMultifileClass(state, multifileClassFqName, filesInMultifileClasses.get(multifileClassFqName), errorHandler);
} }
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
@@ -25,7 +25,6 @@ import com.intellij.util.ArrayUtil;
import com.intellij.util.SmartList; import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtil;
import kotlin.jvm.functions.Function0; import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.Mutable; import org.jetbrains.annotations.Mutable;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -40,6 +39,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils; import org.jetbrains.kotlin.diagnostics.DiagnosticUtils;
import org.jetbrains.kotlin.fileClasses.FileClassesPackage; import org.jetbrains.kotlin.fileClasses.FileClassesPackage;
import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo;
import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.java.JvmAnnotationNames; import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils;
@@ -87,7 +87,11 @@ public class PackageCodegen {
private final PackageParts packageParts; private final PackageParts packageParts;
public PackageCodegen(@NotNull GenerationState state, @NotNull Collection<JetFile> files, @NotNull FqName fqName) { public PackageCodegen(
@NotNull GenerationState state,
@NotNull Collection<JetFile> files,
@NotNull FqName fqName
) {
this.state = state; this.state = state;
this.files = files; this.files = files;
this.packageFragment = getOnlyPackageFragment(fqName); this.packageFragment = getOnlyPackageFragment(fqName);
@@ -219,7 +223,7 @@ public class PackageCodegen {
for (JetFile file : files) { for (JetFile file : files) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
try { try {
ClassBuilder builder = generatePart(file, generateCallableMemberTasks); ClassBuilder builder = generateFile(file, generateCallableMemberTasks);
if (builder != null) { if (builder != null) {
bindings.add(builder.getSerializationBindings()); bindings.add(builder.getSerializationBindings());
} }
@@ -284,14 +288,7 @@ public class PackageCodegen {
Collection<PackageFragmentDescriptor> packageFragments = Lists.newArrayList(); Collection<PackageFragmentDescriptor> packageFragments = Lists.newArrayList();
ContainerUtil.addIfNotNull(packageFragments, packageFragment); ContainerUtil.addIfNotNull(packageFragments, packageFragment);
ContainerUtil.addIfNotNull(packageFragments, compiledPackageFragment); ContainerUtil.addIfNotNull(packageFragments, compiledPackageFragment);
ProtoBuf.Package packageProto = serializer.packageProto(packageFragments, new Function1<DeclarationDescriptor, Boolean>() { ProtoBuf.Package packageProto = serializer.packageProtoWithoutDescriptors().build();
@Override
public Boolean invoke(DeclarationDescriptor descriptor) {
return true;
}
}).build();
//if (packageProto.getMemberCount() == 0) return;
AnnotationVisitor av = v.newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE), true); AnnotationVisitor av = v.newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE), true);
JvmCodegenUtil.writeAbiVersion(av); JvmCodegenUtil.writeAbiVersion(av);
@@ -309,10 +306,19 @@ public class PackageCodegen {
} }
@Nullable @Nullable
private ClassBuilder generatePart(@NotNull JetFile file, @NotNull Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) { private ClassBuilder generateFile(@NotNull JetFile file, @NotNull Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) {
JvmFileClassInfo fileClassInfo = state.getFileClassesProvider().getFileClassInfo(file);
if (fileClassInfo.getIsMultifileClass()) {
Type fileFacadeType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(fileClassInfo.getFacadeClassFqName());
addDelegateToFileClassMemberTasks(file, generateCallableMemberTasks, fileFacadeType);
return null;
}
Type fileClassType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(fileClassInfo.getFileClassFqName());
PackageContext packagePartContext = state.getRootContext().intoPackagePart(packageFragment, fileClassType);
boolean generatePackagePart = false; boolean generatePackagePart = false;
Type packagePartType = FileClassesPackage.getFileClassType(state.getFileClassesProvider(), file);
PackageContext packagePartContext = state.getRootContext().intoPackagePart(packageFragment, packagePartType);
for (JetDeclaration declaration : file.getDeclarations()) { for (JetDeclaration declaration : file.getDeclarations()) {
if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) { if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) {
@@ -327,7 +333,7 @@ public class PackageCodegen {
else if (declaration instanceof JetScript) { else if (declaration instanceof JetScript) {
JetScript script = (JetScript) declaration; JetScript script = (JetScript) declaration;
// SCRIPT: generate script code, should be separate execution branch // SCRIPT: generate script code, should be separate execution branch
if (state.getGenerateDeclaredClassFilter().shouldGenerateScript(script)) { if (state.getGenerateDeclaredClassFilter().shouldGenerateScript(script)) {
ScriptCodegen.createScriptCodegen(script, state, packagePartContext).generate(); ScriptCodegen.createScriptCodegen(script, state, packagePartContext).generate();
} }
@@ -336,15 +342,24 @@ public class PackageCodegen {
if (!generatePackagePart || !state.getGenerateDeclaredClassFilter().shouldGeneratePackagePart(file)) return null; if (!generatePackagePart || !state.getGenerateDeclaredClassFilter().shouldGeneratePackagePart(file)) return null;
String name = packagePartType.getInternalName(); String name = fileClassType.getInternalName();
packageParts.getParts().add(name.substring(name.lastIndexOf('/') + 1)); packageParts.getParts().add(name.substring(name.lastIndexOf('/') + 1));
ClassBuilder builder = state.getFactory().newVisitor(PackagePart(file, packageFragment), packagePartType, file); ClassBuilder builder = state.getFactory().newVisitor(PackagePart(file, packageFragment), fileClassType, file);
new PackagePartCodegen(builder, file, packagePartType, packagePartContext, state).generate(); new PackagePartCodegen(builder, file, fileClassType, packagePartContext, state).generate();
FieldOwnerContext packageFacade = state.getRootContext().intoPackageFacade(packagePartType, packageFragment); addDelegateToFileClassMemberTasks(file, generateCallableMemberTasks, fileClassType);
return builder;
}
private void addDelegateToFileClassMemberTasks(
@NotNull JetFile file,
@NotNull Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks,
@NotNull Type fileClassType
) {
FieldOwnerContext packageFacade = state.getRootContext().intoPackageFacade(fileClassType, packageFragment);
final MemberCodegen<?> memberCodegen = createCodegenForPartOfPackageFacade(packageFacade); final MemberCodegen<?> memberCodegen = createCodegenForPartOfPackageFacade(packageFacade);
for (final JetDeclaration declaration : file.getDeclarations()) { for (final JetDeclaration declaration : file.getDeclarations()) {
@@ -363,8 +378,6 @@ public class PackageCodegen {
); );
} }
} }
return builder;
} }
private MemberCodegen<?> createCodegenForPartOfPackageFacade(@NotNull FieldOwnerContext packageFacade) { private MemberCodegen<?> createCodegenForPartOfPackageFacade(@NotNull FieldOwnerContext packageFacade) {
@@ -437,6 +437,15 @@ public class DescriptorSerializer {
return packageProto(fragments, null); return packageProto(fragments, null);
} }
@NotNull
public ProtoBuf.Package.Builder packageProtoWithoutDescriptors() {
ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
extension.serializePackage(Collections.<PackageFragmentDescriptor>emptyList(), builder, stringTable);
return builder;
}
@NotNull @NotNull
public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments, @Nullable Function1<DeclarationDescriptor, Boolean> skip) { public ProtoBuf.Package.Builder packageProto(@NotNull Collection<PackageFragmentDescriptor> fragments, @Nullable Function1<DeclarationDescriptor, Boolean> skip) {
ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder(); ProtoBuf.Package.Builder builder = ProtoBuf.Package.newBuilder();
@@ -1,4 +1,4 @@
@file:[JvmName("APackage") JvmMultifileClass] @file:[JvmName("A") JvmMultifileClass]
package a package a
inline fun foo(body: () -> String): String = zee(body()) inline fun foo(body: () -> String): String = zee(body())
@@ -1,4 +1,4 @@
@file:[JvmName("APackage") JvmMultifileClass] @file:[JvmName("A") JvmMultifileClass]
package a package a
public fun zee(x: String): String = x public fun zee(x: String): String = x
@@ -0,0 +1,7 @@
import test.TestPackage;
public class Baz {
public static String baz() {
return TestPackage.foo() + TestPackage.bar();
}
}
@@ -0,0 +1,5 @@
@file:JvmName("Util")
@file:JvmMultifileClass
package test
public fun bar(): String = "K"
@@ -0,0 +1,4 @@
@file:[JvmName("Util") JvmMultifileClass]
package test
public fun foo(): String = "O"
@@ -0,0 +1,5 @@
package test
import Baz
fun box(): String = Baz.baz()
@@ -1,5 +1,4 @@
// NB this multifile class should be called "TestPackage" due to the way codegen box tests work. @file:JvmName("Test")
@file:JvmName("TestPackage")
@file:JvmMultifileClass @file:JvmMultifileClass
package test package test
@@ -1,4 +1,4 @@
@file:JvmName("TestPackage") @file:JvmName("Test")
@file:JvmMultifileClass @file:JvmMultifileClass
package test package test
@@ -1,4 +1,4 @@
@file:JvmName("TestPackage") @file:JvmName("Test")
@file:JvmMultifileClass @file:JvmMultifileClass
package test package test
@@ -175,6 +175,12 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege
doTestWithJava(fileName); doTestWithJava(fileName);
} }
@TestMetadata("withPackageFacade")
public void testWithPackageFacade() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/fileClasses/withPackageFacade/");
doTestWithJava(fileName);
}
} }
@TestMetadata("compiler/testData/codegen/boxWithJava/interfaces") @TestMetadata("compiler/testData/codegen/boxWithJava/interfaces")