diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 3c5d57c691b..753b242a924 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -374,6 +374,9 @@ public class FunctionCodegen { if (!KotlinTypeMapper.isAccessor(functionDescriptor)) { genNotNullAssertionsForParameters(new InstructionAdapter(mv), parentCodegen.state, functionDescriptor, frameMap); } + + parentCodegen.beforeMethodBody(mv); + methodEnd = new Label(); context.setMethodEndLabel(methodEnd); strategy.generateBody(mv, frameMap, signature, context, parentCodegen); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java index 6750893a7fa..fdf634d017c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java @@ -336,23 +336,32 @@ public abstract class MemberCodegen", "()V", null, null); - SimpleFunctionDescriptorImpl clInit = - SimpleFunctionDescriptorImpl.create(descriptor, Annotations.Companion.getEMPTY(), Name.special(""), SYNTHESIZED, - KotlinSourceElementKt.toSourceElement(element)); - clInit.initialize(null, null, Collections.emptyList(), - Collections.emptyList(), - DescriptorUtilsKt.getModule(descriptor).getBuiltIns().getUnitType(), - null, Visibilities.PRIVATE); - - this.clInit = new ExpressionCodegen(mv, new FrameMap(), Type.VOID_TYPE, context.intoFunction(clInit), state, this); + DeclarationDescriptor contextDescriptor = context.getContextDescriptor(); + SimpleFunctionDescriptorImpl clInitDescriptor = createClInitFunctionDescriptor(contextDescriptor); + MethodVisitor mv = createClInitMethodVisitor(contextDescriptor); + clInit = new ExpressionCodegen(mv, new FrameMap(), Type.VOID_TYPE, context.intoFunction(clInitDescriptor), state, this); } return clInit; } + @NotNull + protected MethodVisitor createClInitMethodVisitor(@NotNull DeclarationDescriptor contextDescriptor) { + return v.newMethod(JvmDeclarationOriginKt.OtherOrigin(contextDescriptor), ACC_STATIC, "", "()V", null, null); + } + + @NotNull + protected SimpleFunctionDescriptorImpl createClInitFunctionDescriptor(@NotNull DeclarationDescriptor descriptor) { + SimpleFunctionDescriptorImpl clInit = SimpleFunctionDescriptorImpl.create(descriptor, Annotations.Companion.getEMPTY(), + Name.special(""), SYNTHESIZED, KotlinSourceElementKt.toSourceElement(element)); + clInit.initialize(null, null, Collections.emptyList(), + Collections.emptyList(), + DescriptorUtilsKt.getModule(descriptor).getBuiltIns().getUnitType(), + null, Visibilities.PRIVATE); + return clInit; + } + protected void generateInitializers(@NotNull Function0 createCodegen) { NotNullLazyValue codegen = LockBasedStorageManager.NO_LOCKS.createLazyValue(createCodegen); for (KtDeclaration declaration : ((KtDeclarationContainer) element).getDeclarations()) { @@ -370,6 +379,9 @@ public abstract class MemberCodegen, + private val files: Collection, private val facadeFqName: FqName, private val packagePartRegistry: PackagePartRegistry ) { @@ -71,55 +71,105 @@ class MultifileClassCodegen( getDeserializedCallables(compiledPackageFragment) private fun getDeserializedCallables(compiledPackageFragment: PackageFragmentDescriptor) = - compiledPackageFragment.getMemberScope().getContributedDescriptors(DescriptorKindFilter.CALLABLES, MemberScope.ALL_NAME_FILTER).filterIsInstance() + compiledPackageFragment.getMemberScope() + .getContributedDescriptors(DescriptorKindFilter.CALLABLES, MemberScope.ALL_NAME_FILTER) + .filterIsInstance() + + private fun KtFile.getFileClassFqName() = + state.fileClassesProvider.getFileClassInfo(this).fileClassFqName + + private val shouldGeneratePartHierarchy = + state.inheritMultifileParts // TODO support incremental compilation + + private val partInternalNamesSorted = run { + val partInternalNamesSet = hashSetOf() + for (file in files) { + if (file.hasDeclarationsForPartClass()) { + partInternalNamesSet.add(file.getFileClassFqName().toInternalName()) + } + } + compiledPackageFragment?.let { + partInternalNamesSet.addAll(it.partsNames) + } + partInternalNamesSet.sorted() + } + + private val superClassForInheritedPart = run { + val result = hashMapOf() + for (i in 1 ..partInternalNamesSorted.size - 1) { + result[partInternalNamesSorted[i]] = partInternalNamesSorted[i - 1] + } + result + } + + private val delegateGenerationTasks = hashMapOf Unit>() + + private fun getSuperClassForPart(partInternalName: String) = + if (shouldGeneratePartHierarchy) + superClassForInheritedPart[partInternalName] ?: J_L_OBJECT + else + J_L_OBJECT private val classBuilder = ClassBuilderOnDemand { val originFile = files.firstOrNull() - val actualPackageFragment = packageFragment ?: - compiledPackageFragment ?: - throw AssertionError("No package fragment for multifile facade $facadeFqName; files: $files") - val declarationOrigin = MultifileClass(originFile, actualPackageFragment, facadeFqName) - val classBuilder = state.factory.newVisitor(declarationOrigin, facadeClassType, files) - val filesWithCallables = files.filter { it.declarations.any { it is KtNamedFunction || it is KtProperty } } + val actualPackageFragment = packageFragment + ?: compiledPackageFragment + ?: throw AssertionError("No package fragment for multifile facade $facadeFqName; files: $files") - val singleSourceFile = if (previouslyCompiledCallables.isNotEmpty()) null else filesWithCallables.singleOrNull() + val declarationOrigin = MultifileClass(originFile, actualPackageFragment) - classBuilder.defineClass(singleSourceFile, Opcodes.V1_6, - FACADE_CLASS_ATTRIBUTES, - facadeClassType.internalName, - null, "java/lang/Object", ArrayUtil.EMPTY_STRING_ARRAY) - if (singleSourceFile != null) { - classBuilder.visitSource(singleSourceFile.name, null) + val singleSourceFile = + if (previouslyCompiledCallables.isEmpty()) + files.singleOrNull { it.hasDeclarationsForPartClass() } + else + null + + val superClassForFacade = + if (shouldGeneratePartHierarchy) + partInternalNamesSorted.last() + else + J_L_OBJECT + + state.factory.newVisitor(declarationOrigin, facadeClassType, files).apply { + defineClass(singleSourceFile, Opcodes.V1_6, FACADE_CLASS_ATTRIBUTES, + facadeClassType.internalName, null, superClassForFacade, ArrayUtil.EMPTY_STRING_ARRAY) + if (singleSourceFile != null) { + visitSource(singleSourceFile.name, null) + } + + if (shouldGeneratePartHierarchy) { + newMethod(OtherOrigin(actualPackageFragment), Opcodes.ACC_PRIVATE, "", "()V", null, null).apply { + visitCode() + visitVarInsn(Opcodes.ALOAD, 0) + visitMethodInsn(Opcodes.INVOKESPECIAL, superClassForFacade, "", "()V", false) + visitInsn(Opcodes.RETURN) + visitMaxs(1, 1) + visitEnd() + } + } } - - classBuilder } fun generate(errorHandler: CompilationErrorHandler) { - val generateCallableMemberTasks = HashMap Unit>() - val partFqNames = arrayListOf() + assert(delegateGenerationTasks.isEmpty()) { "generate() is called twice for facade class $facadeFqName" } - generateCodeForSourceFiles(errorHandler, generateCallableMemberTasks, partFqNames) + generateCodeForSourceFiles(errorHandler) - generateDelegatesToPreviouslyCompiledParts(generateCallableMemberTasks, partFqNames) + generateDelegatesToPreviouslyCompiledParts() - if (!partFqNames.isEmpty()) { - generateMultifileFacadeClass(generateCallableMemberTasks, partFqNames) + if (!partInternalNamesSorted.isEmpty()) { + generateMultifileFacadeClass() } done() } - private fun generateCodeForSourceFiles( - errorHandler: CompilationErrorHandler, - generateCallableMemberTasks: MutableMap Unit>, - partFqNames: MutableList - ) { + private fun generateCodeForSourceFiles(errorHandler: CompilationErrorHandler) { for (file in files) { ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() try { - generatePart(file, generateCallableMemberTasks, partFqNames) + generatePart(file) state.afterIndependentPart() } catch (e: ProcessCanceledException) { @@ -137,87 +187,103 @@ class MultifileClassCodegen( } } - private fun generateMultifileFacadeClass( - tasks: Map Unit>, - partFqNames: List - ) { - for (member in tasks.keys.sortedWith(MemberComparator.INSTANCE)) { - tasks[member]!!() + private fun generateMultifileFacadeClass() { + for (member in delegateGenerationTasks.keys.sortedWith(MemberComparator.INSTANCE)) { + delegateGenerationTasks[member]!!() } - writeKotlinMultifileFacadeAnnotationIfNeeded(partFqNames) + writeKotlinMultifileFacadeAnnotationIfNeeded() } fun generateClassOrObject(classOrObject: KtClassOrObject, packagePartContext: FieldOwnerContext) { MemberCodegen.genClassOrObject(packagePartContext, classOrObject, state, null) } - private fun generatePart( - file: KtFile, - generateCallableMemberTasks: MutableMap Unit>, - partFqNames: MutableList - ) { - val packageFragment = this.packageFragment ?: - throw AssertionError("File part $file of $facadeFqName: no package fragment") + private fun generatePart(file: KtFile) { + val packageFragment = this.packageFragment + ?: throw AssertionError("File part $file of $facadeFqName: no package fragment") - var generatePart = false - val partClassInfo = state.fileClassesProvider.getFileClassInfo(file) - val partType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(partClassInfo.fileClassFqName) - val partContext = state.rootContext.intoMultifileClassPart(packageFragment, facadeClassType, partType, file) + val partClassFqName = file.getFileClassFqName() + val partInitializerClassFqName = FqName(partClassFqName.asString() + "__Init") + val partType = partClassFqName.toAsmType() + val partInitializerType = partInitializerClassFqName.toAsmType() + val partContext = state.rootContext.intoMultifileClassPart(packageFragment, facadeClassType, partType, partInitializerType, file) + generateNonPartClassDeclarations(file, partContext) + + if (!state.generateDeclaredClassFilter.shouldGeneratePackagePart(file) || !file.hasDeclarationsForPartClass()) return + + val partInternalName = partType.internalName + packagePartRegistry.addPart(partInternalName.substring(partInternalName.lastIndexOf('/') + 1)) + + val builder = state.factory.newVisitor(MultifileClassPart(file, packageFragment), partType, file) + + MultifileClassPartCodegen( + builder, file, packageFragment, + getSuperClassForPart(partInternalName), + shouldGeneratePartHierarchy, + partContext, state + ).generate() + + addDelegateGenerationTasksForDeclarationsInFile(file, packageFragment, partType) + } + + private fun generateNonPartClassDeclarations(file: KtFile, partContext: FieldOwnerContext) { for (declaration in file.declarations) { when (declaration) { - is KtProperty, is KtNamedFunction -> { - generatePart = true - } - is KtClassOrObject -> if (state.generateDeclaredClassFilter.shouldGenerateClass(declaration)) { - generateClassOrObject(declaration, partContext) - } - is KtScript -> if (state.generateDeclaredClassFilter.shouldGenerateScript(declaration)) { - ScriptCodegen.createScriptCodegen(declaration, state, partContext).generate() - } - } - } - - - if (!generatePart || !state.generateDeclaredClassFilter.shouldGeneratePackagePart(file)) return - - partFqNames.add(partClassInfo.fileClassFqName) - - val name = partType.internalName - packagePartRegistry.addPart(name.substring(name.lastIndexOf('/') + 1)) - - val builder = state.factory.newVisitor(MultifileClassPart(file, packageFragment, facadeFqName), partType, file) - - MultifileClassPartCodegen(builder, file, partType, facadeClassType, partContext, state).generate() - - val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType) - val memberCodegen = createCodegenForPartOfMultifileFacade(facadeContext) - for (declaration in file.declarations) { - if (declaration is KtNamedFunction || declaration is KtProperty) { - val descriptor = state.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration) - assert(descriptor is CallableMemberDescriptor) { "Expected callable member, was " + descriptor + " for " + declaration.text } - if (!Visibilities.isPrivate((descriptor as CallableMemberDescriptor).visibility) - && AsmUtil.getVisibilityAccessFlag(descriptor) != Opcodes.ACC_PRIVATE) { - generateCallableMemberTasks.put(descriptor, { memberCodegen.genFunctionOrProperty(declaration) }) - } + is KtClassOrObject -> + if (state.generateDeclaredClassFilter.shouldGenerateClass(declaration)) { + generateClassOrObject(declaration, partContext) + } + is KtScript -> + if (state.generateDeclaredClassFilter.shouldGenerateScript(declaration)) { + ScriptCodegen.createScriptCodegen(declaration, state, partContext).generate() + } } } } - private fun generateDelegatesToPreviouslyCompiledParts( - generateCallableMemberTasks: MutableMap Unit>, - partFqNames: MutableList - ) { - if (compiledPackageFragment == null) return + private fun addDelegateGenerationTasksForDeclarationsInFile(file: KtFile, packageFragment: PackageFragmentDescriptor, partType: Type) { + val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType) + val memberCodegen = createCodegenForDelegatesInMultifileFacade(facadeContext) + for (declaration in file.declarations) { + if (declaration is KtNamedFunction || declaration is KtProperty) { + val descriptor = state.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration) + if (descriptor !is CallableMemberDescriptor) { + throw AssertionError("Expected callable member, was " + descriptor + " for " + declaration.text) + } + addDelegateGenerationTaskIfNeeded(descriptor, { memberCodegen.genFunctionOrProperty(declaration) }) + } + } + } - partFqNames.addAll(compiledPackageFragment.partsNames.map { JvmClassName.byInternalName(it).fqNameForClassNameWithoutDollars }) + private fun shouldGenerateInFacade(descriptor: CallableMemberDescriptor): Boolean { + if (Visibilities.isPrivate(descriptor.visibility)) return false + if (AsmUtil.getVisibilityAccessFlag(descriptor) == Opcodes.ACC_PRIVATE) return false + + if (state.classBuilderMode == ClassBuilderMode.LIGHT_CLASSES) return true + + if (shouldGeneratePartHierarchy) { + if (descriptor !is PropertyDescriptor || !descriptor.isConst) return false + } + + return true + } + + private fun addDelegateGenerationTaskIfNeeded(callable: CallableMemberDescriptor, task: () -> Unit) { + if (shouldGenerateInFacade(callable)) { + delegateGenerationTasks[callable] = task + } + } + + private fun generateDelegatesToPreviouslyCompiledParts() { + if (compiledPackageFragment == null) return for (callable in previouslyCompiledCallables) { val partFqName = JvmFileClassUtil.getPartFqNameForDeserializedCallable(callable) val partType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(partFqName) - generateCallableMemberTasks[callable] = { generateDelegateToCompiledMember(callable, compiledPackageFragment, partType) } + addDelegateGenerationTaskIfNeeded(callable, { generateDelegateToCompiledMember(callable, compiledPackageFragment, partType) }) } } @@ -228,7 +294,7 @@ class MultifileClassCodegen( ) { val context = state.rootContext.intoMultifileClass(compiledPackageFragment, facadeClassType, partType) - val memberCodegen = createCodegenForPartOfMultifileFacade(context) + val memberCodegen = createCodegenForDelegatesInMultifileFacade(context) when (member) { is DeserializedSimpleFunctionDescriptor -> { @@ -254,20 +320,20 @@ class MultifileClassCodegen( } } - private fun writeKotlinMultifileFacadeAnnotationIfNeeded(partFqNames: List) { + private fun writeKotlinMultifileFacadeAnnotationIfNeeded() { if (state.classBuilderMode != ClassBuilderMode.FULL) return if (files.any { it.isScript }) return writeKotlinMetadata(classBuilder, KotlinClassHeader.Kind.MULTIFILE_CLASS) { av -> val arv = av.visitArray(JvmAnnotationNames.METADATA_DATA_FIELD_NAME) - for (internalName in partFqNames.map(AsmUtil::internalNameByFqNameWithoutInnerClasses).sorted()) { + for (internalName in partInternalNamesSorted) { arv.visit(null, internalName) } arv.visitEnd() } } - private fun createCodegenForPartOfMultifileFacade(facadeContext: FieldOwnerContext<*>): MemberCodegen = + private fun createCodegenForDelegatesInMultifileFacade(facadeContext: FieldOwnerContext<*>): MemberCodegen = object : MemberCodegen(state, null, facadeContext, null, classBuilder) { override fun generateDeclaration() = throw UnsupportedOperationException() override fun generateBody() = throw UnsupportedOperationException() @@ -282,15 +348,16 @@ class MultifileClassCodegen( } companion object { + private val J_L_OBJECT = AsmTypes.OBJECT_TYPE.internalName private val FACADE_CLASS_ATTRIBUTES = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER private fun getOnlyPackageFragment(packageFqName: FqName, files: Collection, bindingContext: BindingContext): PackageFragmentDescriptor? { val fragments = SmartList() for (file in files) { val fragment = bindingContext.get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file) - assert(fragment != null) { "package fragment is null for " + file + "\n" + file.text } + ?: throw AssertionError("package fragment is null for " + file + "\n" + file.text) - assert(packageFqName == fragment!!.fqName) { "expected package fq name: " + packageFqName + ", actual: " + fragment.fqName } + assert(packageFqName == fragment.fqName) { "expected package fq name: " + packageFqName + ", actual: " + fragment.fqName } if (!fragments.contains(fragment)) { fragments.add(fragment) @@ -302,6 +369,15 @@ class MultifileClassCodegen( return fragments.firstOrNull() } + private fun KtFile.hasDeclarationsForPartClass() = + declarations.any { it is KtProperty || it is KtFunction } + + private fun FqName.toInternalName() = + AsmUtil.internalNameByFqNameWithoutInnerClasses(this) + + private fun FqName.toAsmType() = + AsmUtil.asmTypeByFqNameWithoutInnerClasses(this) + private fun getCompiledPackageFragment(facadeFqName: FqName, state: GenerationState): IncrementalPackageFragmentProvider.IncrementalPackageFragment.IncrementalMultifileClassPackageFragment? { if (!IncrementalCompilation.isEnabled()) return null diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassPartCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassPartCodegen.kt index a6bdb2dd54c..9e01d397be8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassPartCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassPartCodegen.kt @@ -17,44 +17,104 @@ package org.jetbrains.kotlin.codegen import com.intellij.util.ArrayUtil -import org.jetbrains.kotlin.codegen.context.FieldOwnerContext +import org.jetbrains.kotlin.codegen.context.MultifileClassPartContext import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass +import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin import org.jetbrains.kotlin.serialization.DescriptorSerializer +import org.jetbrains.org.objectweb.asm.MethodVisitor import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode +import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode import java.util.* class MultifileClassPartCodegen( v: ClassBuilder, file: KtFile, - private val filePartType: Type, - private val multifileClassType: Type, - partContext: FieldOwnerContext<*>, + private val packageFragment: PackageFragmentDescriptor, + private val superClassInternalName: String, + private val shouldGeneratePartHierarchy: Boolean, + private val partContext: MultifileClassPartContext, state: GenerationState ) : MemberCodegen(state, null, partContext, file, v) { + private val partType = partContext.filePartType + private val facadeClassType = partContext.multifileClassType + private val staticInitClassType = Type.getObjectType(partType.internalName + STATIC_INIT_CLASS_SUFFIX) + + private val partClassAttributes = + if (shouldGeneratePartHierarchy) + OPEN_PART_CLASS_ATTRIBUTES + else + FINAL_PART_CLASS_ATTRIBUTES + + private fun ClassBuilder.newSpecialMethod(originDescriptor: DeclarationDescriptor, name: String) = + newMethod(OtherOrigin(originDescriptor), Opcodes.ACC_STATIC or Opcodes.ACC_SUPER, name, "()V", null, null) + + private val staticInitClassBuilder = ClassBuilderOnDemand { + state.factory.newVisitor(MultifileClass(file, packageFragment), staticInitClassType, file).apply { + defineClass(file, Opcodes.V1_6, STATE_INITIALIZER_CLASS_ATTRIBUTES, + staticInitClassType.internalName, null, "java/lang/Object", ArrayUtil.EMPTY_STRING_ARRAY) + + visitSource(file.name, null) + } + } + + private val requiresDeferredStaticInitialization = + shouldGeneratePartHierarchy && file.declarations.any { + it is KtProperty && shouldInitializeProperty(it) + } + override fun generate() { if (state.classBuilderMode == ClassBuilderMode.LIGHT_CLASSES) return + super.generate() + + if (shouldGeneratePartHierarchy) { + v.newMethod(OtherOrigin(packageFragment), Opcodes.ACC_PUBLIC, "", "()V", null, null).apply { + visitCode() + visitVarInsn(Opcodes.ALOAD, 0) + visitMethodInsn(Opcodes.INVOKESPECIAL, superClassInternalName, "", "()V", false) + visitInsn(Opcodes.RETURN) + visitMaxs(1, 1) + visitEnd() + } + } + + if (requiresDeferredStaticInitialization) { + staticInitClassBuilder.apply { + newSpecialMethod(packageFragment, CLINIT_TRIGGER_NAME).apply { + visitCode() + visitInsn(Opcodes.RETURN) + visitMaxs(0, 0) + visitEnd() + } + + newSpecialMethod(packageFragment, "").apply { + visitCode() + visitMethodInsn(Opcodes.INVOKESTATIC, partType.internalName, DEFERRED_PART_CLINIT_NAME, "()V", false) + visitInsn(Opcodes.RETURN) + visitMaxs(0, 0) + visitEnd() + } + } + } } override fun generateDeclaration() { - v.defineClass(element, Opcodes.V1_6, - Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_SUPER, - filePartType.internalName, - null, - "java/lang/Object", - ArrayUtil.EMPTY_STRING_ARRAY) + v.defineClass(element, Opcodes.V1_6, partClassAttributes, partType.internalName, null, superClassInternalName, ArrayUtil.EMPTY_STRING_ARRAY) v.visitSource(element.name, null) - generatePropertyMetadataArrayFieldIfNeeded(filePartType) + generatePropertyMetadataArrayFieldIfNeeded(partType) } override fun generateBody() { @@ -69,6 +129,20 @@ class MultifileClassPartCodegen( } } + override fun createClInitMethodVisitor(contextDescriptor: DeclarationDescriptor): MethodVisitor = + if (requiresDeferredStaticInitialization) + v.newSpecialMethod(contextDescriptor, DEFERRED_PART_CLINIT_NAME) + else + super.createClInitMethodVisitor(contextDescriptor) + + override fun done() { + super.done() + + if (staticInitClassBuilder.isComputed) { + staticInitClassBuilder.done() + } + } + override fun generateKotlinMetadataAnnotation() { val members = ArrayList() for (declaration in element.declarations) { @@ -89,11 +163,34 @@ class MultifileClassPartCodegen( writeKotlinMetadata(v, KotlinClassHeader.Kind.MULTIFILE_CLASS_PART) { av -> AsmUtil.writeAnnotationData(av, serializer, packageProto) - av.visit(JvmAnnotationNames.METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME, multifileClassType.internalName) + av.visit(JvmAnnotationNames.METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME, facadeClassType.internalName) } } override fun generateSyntheticParts() { generateSyntheticAccessors() } + + override fun beforeMethodBody(mv: MethodVisitor) { + if (requiresDeferredStaticInitialization) { + mv.visitMethodInsn(Opcodes.INVOKESTATIC, staticInitClassType.internalName, CLINIT_TRIGGER_NAME, "()V", false) + } + } + + companion object { + private val OPEN_PART_CLASS_ATTRIBUTES = Opcodes.ACC_SUPER + private val FINAL_PART_CLASS_ATTRIBUTES = Opcodes.ACC_SYNTHETIC or Opcodes.ACC_SUPER or Opcodes.ACC_FINAL + private val STATE_INITIALIZER_CLASS_ATTRIBUTES = Opcodes.ACC_SYNTHETIC or Opcodes.ACC_SUPER or Opcodes.ACC_FINAL + + private val STATIC_INIT_CLASS_SUFFIX = "__Clinit" + private val CLINIT_TRIGGER_NAME = "\$\$clinitTrigger" + private val DEFERRED_PART_CLINIT_NAME = "\$\$clinit" + + @JvmStatic fun isStaticInitTrigger(insn: AbstractInsnNode) = + insn.opcode == Opcodes.INVOKESTATIC + && insn is MethodInsnNode + && insn.owner.endsWith(STATIC_INIT_CLASS_SUFFIX) + && insn.name == CLINIT_TRIGGER_NAME + && insn.desc == "()V" + } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java index 5ad439bae76..24ce90bce0d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotated; import org.jetbrains.kotlin.descriptors.annotations.AnnotationSplitter; import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget; import org.jetbrains.kotlin.descriptors.annotations.Annotations; +import org.jetbrains.kotlin.fileClasses.JvmFileClassUtilKt; import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; @@ -184,6 +185,9 @@ public class PropertyCodegen { // Companion object properties always should have accessors, because their backing fields are moved/copied to the outer class if (isCompanionObject(descriptor.getContainingDeclaration())) return true; + // Non-const properties from multifile classes have accessors regardless of visibility + if (!descriptor.isConst() && JvmFileClassUtilKt.isInsideJvmMultifileClassFile(declaration)) return true; + // Private class properties have accessors only in cases when those accessors are non-trivial if (Visibilities.isPrivate(descriptor.getVisibility())) { return !isDefaultAccessor; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java index 3361ed51f4a..d72aca11cb5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java @@ -250,13 +250,14 @@ public abstract class CodegenContext { } @NotNull - public FieldOwnerContext intoMultifileClassPart( + public MultifileClassPartContext intoMultifileClassPart( @NotNull PackageFragmentDescriptor descriptor, @NotNull Type multifileClassType, @NotNull Type filePartType, + @NotNull Type filePartInitializerType, @NotNull KtFile sourceFile ) { - return new MultifileClassPartContext(descriptor, this, multifileClassType, filePartType, sourceFile); + return new MultifileClassPartContext(descriptor, this, multifileClassType, filePartType, filePartInitializerType, sourceFile); } @NotNull diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassPartContext.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassPartContext.java index 87ac6b61cb6..869968706bd 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassPartContext.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassPartContext.java @@ -24,16 +24,19 @@ import org.jetbrains.org.objectweb.asm.Type; public class MultifileClassPartContext extends MultifileClassContextBase implements DelegatingToPartContext, FacadePartWithSourceFile { private final KtFile sourceFile; + private final Type partInitializerType; public MultifileClassPartContext( PackageFragmentDescriptor descriptor, CodegenContext parent, Type multifileClassType, Type filePartType, + Type partInitializerType, @NotNull KtFile sourceFile ) { super(descriptor, parent, multifileClassType, filePartType); this.sourceFile = sourceFile; + this.partInitializerType = partInitializerType; } @Nullable @@ -42,6 +45,11 @@ public class MultifileClassPartContext extends MultifileClassContextBase impleme return getFilePartType(); } + @NotNull + public Type getPartInitializerType() { + return partInitializerType; + } + @NotNull @Override public KtFile getSourceFile() { 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 bcaad80282f..3e84a4946a1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java @@ -66,9 +66,7 @@ import java.util.*; import static org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags; import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive; -import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.API; -import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker; -import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.getConstant; +import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.*; import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral; public class InlineCodegen extends CallGenerator { @@ -395,6 +393,7 @@ public class InlineCodegen extends CallGenerator { List infos = MethodInliner.processReturns(adapter, labelOwner, true, null); generateAndInsertFinallyBlocks(adapter, infos, ((StackValue.Local)remapper.remap(parameters.getArgsSizeOnStack() + 1).value).index); + removeStaticInitializationTrigger(adapter); removeFinallyMarkers(adapter); adapter.accept(new MethodBodyVisitor(codegen.v)); @@ -404,6 +403,21 @@ public class InlineCodegen extends CallGenerator { return result; } + private static void removeStaticInitializationTrigger(MethodNode methodNode) { + InsnList insnList = methodNode.instructions; + AbstractInsnNode insn = insnList.getFirst(); + while (insn != null) { + if (MultifileClassPartCodegen.isStaticInitTrigger(insn)) { + AbstractInsnNode clinitTriggerCall = insn; + insn = insn.getNext(); + insnList.remove(clinitTriggerCall); + } + else { + insn = insn.getNext(); + } + } + } + private InlineCallSiteInfo getInlineCallSiteInfo() { MethodContext context = codegen.getContext(); MemberCodegen parentCodegen = codegen.getParentCodegen(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java index 3e0b4060f53..8d6d34dda83 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java @@ -973,13 +973,20 @@ public class KotlinTypeMapper { } @NotNull - private String updateMemberNameIfInternal(@NotNull String name, @NotNull CallableMemberDescriptor descriptor) { + private String updateMemberNameIfInternal(@NotNull String name, @NotNull CallableMemberDescriptor descriptor) { if (descriptor.getContainingDeclaration() instanceof ScriptDescriptor) { //script properties should be public return name; } if (DescriptorUtils.isTopLevelDeclaration(descriptor)) { + if (Visibilities.isPrivate(descriptor.getVisibility()) && !(descriptor instanceof ConstructorDescriptor) && !"".equals(name)) { + KtFile containingFile = DescriptorToSourceUtils.getContainingFile(descriptor); + assert containingFile != null : "Private descriptor accessed outside of corresponding file scope: " + descriptor; + if (JvmFileClassUtil.isFromMultifileClass(containingFile, descriptor)) { + return name + "$" + JvmAbi.sanitizeAsJavaIdentifier(containingFile.getName()); + } + } return name; } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/fileClasses/JvmFileClassUtil.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/fileClasses/JvmFileClassUtil.kt index d3e3259bb95..499fdc3eed3 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/fileClasses/JvmFileClassUtil.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/fileClasses/JvmFileClassUtil.kt @@ -18,12 +18,15 @@ package org.jetbrains.kotlin.fileClasses import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.load.java.descriptors.getImplClassNameForDeserialized import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor object JvmFileClassUtil { @@ -84,14 +87,21 @@ object JvmFileClassUtil { it.calleeExpression?.constructorReferenceExpression?.getReferencedName() == shortName } - @JvmStatic - private fun getLiteralStringFromRestrictedConstExpression(argumentExpression: KtExpression?): String? { + @JvmStatic private fun getLiteralStringFromRestrictedConstExpression(argumentExpression: KtExpression?): String? { val stringTemplate = argumentExpression as? KtStringTemplateExpression ?: return null val stringTemplateEntries = stringTemplate.entries if (stringTemplateEntries.size != 1) return null val singleEntry = stringTemplateEntries[0] as? KtLiteralStringTemplateEntry ?: return null return singleEntry.text } + + @JvmStatic fun isFromMultifileClass(declarationElement: KtElement, descriptor: DeclarationDescriptor): Boolean { + if (DescriptorUtils.isTopLevelDeclaration(descriptor)) { + val fileClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(declarationElement.getContainingKtFile()) + return fileClassInfo.withJvmMultifileClass + } + return false + } } internal class ParsedJvmFileClassAnnotations(val name: String, val multipleFiles: Boolean) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmDeclarationOrigin.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmDeclarationOrigin.kt index b57c02fdb52..866538c02d6 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmDeclarationOrigin.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmDeclarationOrigin.kt @@ -18,7 +18,6 @@ package org.jetbrains.kotlin.resolve.jvm.diagnostics import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils @@ -68,9 +67,9 @@ fun PackagePart(file: KtFile, descriptor: PackageFragmentDescriptor): JvmDeclara /** * @param representativeFile one of the files representing this multifile class (will be used for diagnostics) */ -fun MultifileClass(representativeFile: KtFile?, descriptor: PackageFragmentDescriptor, multifileClassFqName: FqName): JvmDeclarationOrigin = +fun MultifileClass(representativeFile: KtFile?, descriptor: PackageFragmentDescriptor): JvmDeclarationOrigin = JvmDeclarationOrigin(MULTIFILE_CLASS, representativeFile, descriptor) -fun MultifileClassPart(file: KtFile, descriptor: PackageFragmentDescriptor, multifileClassFqName: FqName): JvmDeclarationOrigin = +fun MultifileClassPart(file: KtFile, descriptor: PackageFragmentDescriptor): JvmDeclarationOrigin = JvmDeclarationOrigin(MULTIFILE_CLASS_PART, file, descriptor) fun TraitImpl(element: KtClassOrObject, descriptor: ClassDescriptor): JvmDeclarationOrigin = JvmDeclarationOrigin(INTERFACE_DEFAULT_IMPL, element, descriptor) diff --git a/compiler/testData/codegen/box/multifileClasses/multifileClassPartsInitialization.kt b/compiler/testData/codegen/box/multifileClasses/multifileClassPartsInitialization.kt index 33b98e5aaf5..ad91a505238 100644 --- a/compiler/testData/codegen/box/multifileClasses/multifileClassPartsInitialization.kt +++ b/compiler/testData/codegen/box/multifileClasses/multifileClassPartsInitialization.kt @@ -25,3 +25,11 @@ val K: String = "K" package a val OK: String = O + K + +// FILE: irrelevant.kt + +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +val X: Nothing = throw AssertionError("X should not be initialized") + diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/callableRefToFun.kt b/compiler/testData/codegen/box/multifileClasses/optimized/callableRefToFun.kt new file mode 100644 index 00000000000..6cca423ba20 --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/callableRefToFun.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: box.kt + +import a.* + +fun box(): String = (::ok)() + +// FILE: part1.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +fun ok() = "OK" \ No newline at end of file diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/callableRefToInternalValInline.kt b/compiler/testData/codegen/box/multifileClasses/optimized/callableRefToInternalValInline.kt new file mode 100644 index 00000000000..c9a33db11d7 --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/callableRefToInternalValInline.kt @@ -0,0 +1,16 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: box.kt + +import a.* + +fun box(): String = okInline() + +// FILE: part1.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +internal val ok = run { "OK" } + +internal inline fun okInline() = + ::ok.get() \ No newline at end of file diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/callableRefToPrivateVal.kt b/compiler/testData/codegen/box/multifileClasses/optimized/callableRefToPrivateVal.kt new file mode 100644 index 00000000000..e31551e8efc --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/callableRefToPrivateVal.kt @@ -0,0 +1,17 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: box.kt + +import a.* + +fun box(): String = OK.okRef.get() + +// FILE: part1.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +private val ok = run { "OK" } + +object OK { + val okRef = ::ok +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/callableRefToVal.kt b/compiler/testData/codegen/box/multifileClasses/optimized/callableRefToVal.kt new file mode 100644 index 00000000000..d2f0e94ec42 --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/callableRefToVal.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: box.kt + +import a.* + +fun box(): String = ::OK.get() + +// FILE: part1.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +val OK = run { "OK" } \ No newline at end of file diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/calls.kt b/compiler/testData/codegen/box/multifileClasses/optimized/calls.kt new file mode 100644 index 00000000000..800b7d555d3 --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/calls.kt @@ -0,0 +1,29 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: Baz.java + +public class Baz { + public static String baz() { + return Util.foo() + Util.bar(); + } +} + +// FILE: bar.kt + +@file:JvmName("Util") +@file:JvmMultifileClass +public fun bar(): String = barx() + +public fun foox(): String = "O" + +// FILE: foo.kt + +@file:JvmName("Util") +@file:JvmMultifileClass +public fun foo(): String = foox() + +public fun barx(): String = "K" + +// FILE: test.kt + +fun box(): String = Baz.baz() diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/deferredStaticInitialization.kt b/compiler/testData/codegen/box/multifileClasses/optimized/deferredStaticInitialization.kt new file mode 100644 index 00000000000..58a2887956c --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/deferredStaticInitialization.kt @@ -0,0 +1,39 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: box.kt + +import a.* + +fun box(): String = OK + +// FILE: part1.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +val O = run { "O" } + +// FILE: part2.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +const val K = "K" + +// FILE: part3.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +val OK: String = run { O + K } + +// FILE: irrelevantPart.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +val X1: Nothing = + throw AssertionError("X1 should not be initialized") + +// FILE: reallyIrrelevantPart.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +val X2: Nothing = + throw AssertionError("X2 should not be initialized") \ No newline at end of file diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/delegatedVal.kt b/compiler/testData/codegen/box/multifileClasses/optimized/delegatedVal.kt new file mode 100644 index 00000000000..5e6549d39a5 --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/delegatedVal.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: box.kt + +import a.* + +fun box(): String = OK + +// FILE: part1.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +val OK: String by lazy { "OK" } \ No newline at end of file diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/initializePrivateVal.kt b/compiler/testData/codegen/box/multifileClasses/optimized/initializePrivateVal.kt new file mode 100644 index 00000000000..ca54134457f --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/initializePrivateVal.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: box.kt + +import a.* + +fun box(): String = ok() + +// FILE: part1.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +private val OK = run { "OK" } + +fun ok() = OK \ No newline at end of file diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/initializePublicVal.kt b/compiler/testData/codegen/box/multifileClasses/optimized/initializePublicVal.kt new file mode 100644 index 00000000000..c8cbb16a74e --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/initializePublicVal.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: box.kt + +import a.* + +fun box(): String = OK + +// FILE: part1.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +public val OK = run { "OK" } diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/overlappingFuns.kt b/compiler/testData/codegen/box/multifileClasses/optimized/overlappingFuns.kt new file mode 100644 index 00000000000..668fe84e269 --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/overlappingFuns.kt @@ -0,0 +1,27 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: box.kt + +import a.* + +fun box(): String = ok() + +// FILE: part1.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +private fun overlapping() = "oops #1" + +// FILE: part2.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +fun overlapping() = "OK" + +fun ok() = overlapping() + +// FILE: part3.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +private fun overlapping() = "oops #2" diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/overlappingVals.kt b/compiler/testData/codegen/box/multifileClasses/optimized/overlappingVals.kt new file mode 100644 index 00000000000..f3bb25d83fb --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/overlappingVals.kt @@ -0,0 +1,25 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: box.kt + +import a.* + +fun box(): String = overlapping + +// FILE: part1.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +private val overlapping = run { "oops #1" } + +// FILE: part2.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +val overlapping = run { "OK" } + +// FILE: part3.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +private val overlapping = run { "oops #2" } diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/valAccessFromInlineFunCalledFromJava.kt b/compiler/testData/codegen/box/multifileClasses/optimized/valAccessFromInlineFunCalledFromJava.kt new file mode 100644 index 00000000000..35ebe2825a7 --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/valAccessFromInlineFunCalledFromJava.kt @@ -0,0 +1,27 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: box.kt + +import a.* + +fun box(): String = J.ok() + +// FILE: part1.kt +@file:[JvmName("MC") JvmMultifileClass] +package a + +val O = run { "O" } +const val K = "K" + +inline fun ok(): String { + return O + K +} + +// FILE: J.java +import a.MC; + +public class J { + public static String ok() { + return MC.ok(); + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/valAccessFromInlinedToDifferentPackage.kt b/compiler/testData/codegen/box/multifileClasses/optimized/valAccessFromInlinedToDifferentPackage.kt new file mode 100644 index 00000000000..1fa15e51af7 --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/valAccessFromInlinedToDifferentPackage.kt @@ -0,0 +1,19 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: box.kt + +import a.* + +fun box(): String = ok {} + +// FILE: part1.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +val O = run { "O" } +const val K = "K" + +inline fun ok(block: () -> Unit): String { + block() + return O + K +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/multifileClasses/optimized/valWithAccessor.kt b/compiler/testData/codegen/box/multifileClasses/optimized/valWithAccessor.kt new file mode 100644 index 00000000000..ada078b252e --- /dev/null +++ b/compiler/testData/codegen/box/multifileClasses/optimized/valWithAccessor.kt @@ -0,0 +1,17 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: box.kt + +import a.* + +fun box(): String = OK().ok + +// FILE: part1.kt +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +private val reallyOk = run { "OK" } + +class OK() { + val ok = reallyOk +} diff --git a/compiler/testData/codegen/boxInline/multifileClasses/inlineFromOptimizedMultifileClass.kt b/compiler/testData/codegen/boxInline/multifileClasses/inlineFromOptimizedMultifileClass.kt new file mode 100644 index 00000000000..a64de1c3012 --- /dev/null +++ b/compiler/testData/codegen/boxInline/multifileClasses/inlineFromOptimizedMultifileClass.kt @@ -0,0 +1,22 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: 1.kt + +@file:[JvmName("MultifileClass") JvmMultifileClass] +package a + +inline fun foo(body: () -> String): String = bar(body()) + +public fun bar(x: String): String = x + +inline fun inlineOnly(x: Any?): Boolean = x is T + +// FILE: 2.kt + +import a.foo +import a.inlineOnly + +fun box(): String { + if (!a.inlineOnly("OK")) return "fail 1" + return foo { "OK" } +} diff --git a/compiler/testData/codegen/bytecodeListing/InlineOnlyMultifile.txt b/compiler/testData/codegen/bytecodeListing/InlineOnlyMultifile.txt index 9eed46549fb..489d4bae512 100644 --- a/compiler/testData/codegen/bytecodeListing/InlineOnlyMultifile.txt +++ b/compiler/testData/codegen/bytecodeListing/InlineOnlyMultifile.txt @@ -7,4 +7,4 @@ public final class test/Foo { synthetic final class test/Foo__InlineOnlyMultifileKt { public final static method foo(): void private final static method inlineOnly(): void -} +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/emptyMultifileFacade.txt b/compiler/testData/codegen/bytecodeListing/emptyMultifileFacade.txt index 1c3cb978a8c..054e3710133 100644 --- a/compiler/testData/codegen/bytecodeListing/emptyMultifileFacade.txt +++ b/compiler/testData/codegen/bytecodeListing/emptyMultifileFacade.txt @@ -3,5 +3,5 @@ public final class test/Foo @kotlin.Metadata synthetic final class test/Foo__EmptyMultifileFacadeKt { - private final static method privateOnly(): void -} + private final static method privateOnly$emptyMultifileFacade_kt(): void +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/multifileClasses/optimizedMultifileClassFacadeMethods.kt b/compiler/testData/codegen/bytecodeText/multifileClasses/optimizedMultifileClassFacadeMethods.kt new file mode 100644 index 00000000000..0753e91c07b --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/multifileClasses/optimizedMultifileClassFacadeMethods.kt @@ -0,0 +1,24 @@ +// WITH_RUNTIME +// KOTLIN_CONFIGURATION_FLAGS: +JVM.INHERIT_MULTIFILE_PARTS +// FILE: bar.kt + +@file:JvmName("Util") +@file:JvmMultifileClass +public fun bar(): String = barx() + +public fun foox(): String = "O" + +// FILE: foo.kt + +@file:JvmName("Util") +@file:JvmMultifileClass +public fun foo(): String = foox() + +public fun barx(): String = "K" + +// @Util.class: +// 1 public final class Util extends Util__FooKt +// 0 public final static foo\(\) +// 0 public final static foox\(\) +// 0 public final static bar\(\) +// 0 public final static barx\(\) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 5ac6f7380ef..fa794e91d74 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -8469,6 +8469,99 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/samePartNameDifferentFacades.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/codegen/box/multifileClasses/optimized") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Optimized extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInOptimized() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/multifileClasses/optimized"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("callableRefToFun.kt") + public void testCallableRefToFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/callableRefToFun.kt"); + doTest(fileName); + } + + @TestMetadata("callableRefToInternalValInline.kt") + public void testCallableRefToInternalValInline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/callableRefToInternalValInline.kt"); + doTest(fileName); + } + + @TestMetadata("callableRefToPrivateVal.kt") + public void testCallableRefToPrivateVal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/callableRefToPrivateVal.kt"); + doTest(fileName); + } + + @TestMetadata("callableRefToVal.kt") + public void testCallableRefToVal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/callableRefToVal.kt"); + doTest(fileName); + } + + @TestMetadata("calls.kt") + public void testCalls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/calls.kt"); + doTest(fileName); + } + + @TestMetadata("deferredStaticInitialization.kt") + public void testDeferredStaticInitialization() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/deferredStaticInitialization.kt"); + doTest(fileName); + } + + @TestMetadata("delegatedVal.kt") + public void testDelegatedVal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/delegatedVal.kt"); + doTest(fileName); + } + + @TestMetadata("initializePrivateVal.kt") + public void testInitializePrivateVal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/initializePrivateVal.kt"); + doTest(fileName); + } + + @TestMetadata("initializePublicVal.kt") + public void testInitializePublicVal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/initializePublicVal.kt"); + doTest(fileName); + } + + @TestMetadata("overlappingFuns.kt") + public void testOverlappingFuns() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/overlappingFuns.kt"); + doTest(fileName); + } + + @TestMetadata("overlappingVals.kt") + public void testOverlappingVals() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/overlappingVals.kt"); + doTest(fileName); + } + + @TestMetadata("valAccessFromInlineFunCalledFromJava.kt") + public void testValAccessFromInlineFunCalledFromJava() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/valAccessFromInlineFunCalledFromJava.kt"); + doTest(fileName); + } + + @TestMetadata("valAccessFromInlinedToDifferentPackage.kt") + public void testValAccessFromInlinedToDifferentPackage() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/valAccessFromInlinedToDifferentPackage.kt"); + doTest(fileName); + } + + @TestMetadata("valWithAccessor.kt") + public void testValWithAccessor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/multifileClasses/optimized/valWithAccessor.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/codegen/box/nonLocalReturns") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java index df3cda8c680..3000b7cd2d1 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java @@ -916,6 +916,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/multifileClasses"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("inlineFromOptimizedMultifileClass.kt") + public void testInlineFromOptimizedMultifileClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/multifileClasses/inlineFromOptimizedMultifileClass.kt"); + doTest(fileName); + } + @TestMetadata("inlineFromOtherPackage.kt") public void testInlineFromOtherPackage() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/multifileClasses/inlineFromOtherPackage.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 64274044652..73e38b9ed6c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1007,6 +1007,21 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { } } + @TestMetadata("compiler/testData/codegen/bytecodeText/multifileClasses") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class MultifileClasses extends AbstractBytecodeTextTest { + public void testAllFilesPresentInMultifileClasses() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/multifileClasses"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("optimizedMultifileClassFacadeMethods.kt") + public void testOptimizedMultifileClassFacadeMethods() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/multifileClasses/optimizedMultifileClassFacadeMethods.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeText/signature") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java index a0ae2d93302..4d36e097c56 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -916,6 +916,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/multifileClasses"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("inlineFromOptimizedMultifileClass.kt") + public void testInlineFromOptimizedMultifileClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/multifileClasses/inlineFromOptimizedMultifileClass.kt"); + doTest(fileName); + } + @TestMetadata("inlineFromOtherPackage.kt") public void testInlineFromOtherPackage() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/multifileClasses/inlineFromOtherPackage.kt");