KT-11410: Class hierarchy for parts/facade of multi-file class.

Preserve static initialization semantics for parts by introducing a special "clinit trigger" class.
Insert "static initialization trigger" call to every method of a part class, remove this call on inline.
Always mangle names for private functions in multifile class parts to avoid resolution clashes on inheritance.

NB in codegen tests initializers for all non-const vals are wrapped in 'run { ... }',
so that the initializer is not a constant expression, and some static initialization code should be generated.
This commit is contained in:
Dmitry Petrov
2016-03-16 12:06:18 +03:00
parent 0ccaf43e2c
commit db58ebc4b2
34 changed files with 823 additions and 135 deletions
@@ -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);
@@ -336,23 +336,32 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
}
@NotNull
protected ExpressionCodegen createOrGetClInitCodegen() {
DeclarationDescriptor descriptor = context.getContextDescriptor();
protected final ExpressionCodegen createOrGetClInitCodegen() {
if (clInit == null) {
MethodVisitor mv = v.newMethod(JvmDeclarationOriginKt.OtherOrigin(descriptor), ACC_STATIC, "<clinit>", "()V", null, null);
SimpleFunctionDescriptorImpl clInit =
SimpleFunctionDescriptorImpl.create(descriptor, Annotations.Companion.getEMPTY(), Name.special("<clinit>"), SYNTHESIZED,
KotlinSourceElementKt.toSourceElement(element));
clInit.initialize(null, null, Collections.<TypeParameterDescriptor>emptyList(),
Collections.<ValueParameterDescriptor>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, "<clinit>", "()V", null, null);
}
@NotNull
protected SimpleFunctionDescriptorImpl createClInitFunctionDescriptor(@NotNull DeclarationDescriptor descriptor) {
SimpleFunctionDescriptorImpl clInit = SimpleFunctionDescriptorImpl.create(descriptor, Annotations.Companion.getEMPTY(),
Name.special("<clinit>"), SYNTHESIZED, KotlinSourceElementKt.toSourceElement(element));
clInit.initialize(null, null, Collections.<TypeParameterDescriptor>emptyList(),
Collections.<ValueParameterDescriptor>emptyList(),
DescriptorUtilsKt.getModule(descriptor).getBuiltIns().getUnitType(),
null, Visibilities.PRIVATE);
return clInit;
}
protected void generateInitializers(@NotNull Function0<ExpressionCodegen> createCodegen) {
NotNullLazyValue<ExpressionCodegen> codegen = LockBasedStorageManager.NO_LOCKS.createLazyValue(createCodegen);
for (KtDeclaration declaration : ((KtDeclarationContainer) element).getDeclarations()) {
@@ -370,6 +379,9 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
}
}
public void beforeMethodBody(@NotNull MethodVisitor mv) {
}
private void initializeProperty(@NotNull ExpressionCodegen codegen, @NotNull KtProperty property) {
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(VARIABLE, property);
assert propertyDescriptor != null;
@@ -382,7 +394,7 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
propValue.store(codegen.gen(initializer), codegen.v);
}
private boolean shouldInitializeProperty(@NotNull KtProperty property) {
protected boolean shouldInitializeProperty(@NotNull KtProperty property) {
if (!property.hasDelegateExpressionOrInitializer()) return false;
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(VARIABLE, property);
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
@@ -37,7 +38,7 @@ import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStat
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.MemberComparator
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClassPart
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
@@ -50,11 +51,10 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import java.util.*
class MultifileClassCodegen(
private val state: GenerationState,
val files: Collection<KtFile>,
private val files: Collection<KtFile>,
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<DeserializedCallableMemberDescriptor>()
compiledPackageFragment.getMemberScope()
.getContributedDescriptors(DescriptorKindFilter.CALLABLES, MemberScope.ALL_NAME_FILTER)
.filterIsInstance<DeserializedCallableMemberDescriptor>()
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<String>()
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<String, String>()
for (i in 1 ..partInternalNamesSorted.size - 1) {
result[partInternalNamesSorted[i]] = partInternalNamesSorted[i - 1]
}
result
}
private val delegateGenerationTasks = hashMapOf<CallableMemberDescriptor, () -> 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, "<init>", "()V", null, null).apply {
visitCode()
visitVarInsn(Opcodes.ALOAD, 0)
visitMethodInsn(Opcodes.INVOKESPECIAL, superClassForFacade, "<init>", "()V", false)
visitInsn(Opcodes.RETURN)
visitMaxs(1, 1)
visitEnd()
}
}
}
classBuilder
}
fun generate(errorHandler: CompilationErrorHandler) {
val generateCallableMemberTasks = HashMap<CallableMemberDescriptor, () -> Unit>()
val partFqNames = arrayListOf<FqName>()
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<CallableMemberDescriptor, () -> Unit>,
partFqNames: MutableList<FqName>
) {
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<CallableMemberDescriptor, () -> Unit>,
partFqNames: List<FqName>
) {
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<PackageFragmentDescriptor>) {
MemberCodegen.genClassOrObject(packagePartContext, classOrObject, state, null)
}
private fun generatePart(
file: KtFile,
generateCallableMemberTasks: MutableMap<CallableMemberDescriptor, () -> Unit>,
partFqNames: MutableList<FqName>
) {
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<PackageFragmentDescriptor>) {
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<CallableMemberDescriptor, () -> Unit>,
partFqNames: MutableList<FqName>
) {
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<FqName>) {
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<KtFile> =
private fun createCodegenForDelegatesInMultifileFacade(facadeContext: FieldOwnerContext<*>): MemberCodegen<KtFile> =
object : MemberCodegen<KtFile>(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<KtFile>, bindingContext: BindingContext): PackageFragmentDescriptor? {
val fragments = SmartList<PackageFragmentDescriptor>()
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
@@ -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<KtFile>(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, "<init>", "()V", null, null).apply {
visitCode()
visitVarInsn(Opcodes.ALOAD, 0)
visitMethodInsn(Opcodes.INVOKESPECIAL, superClassInternalName, "<init>", "()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, "<clinit>").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<DeclarationDescriptor>()
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"
}
}
@@ -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;
@@ -250,13 +250,14 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
}
@NotNull
public FieldOwnerContext<PackageFragmentDescriptor> 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
@@ -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() {
@@ -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<MethodInliner.PointForExternalFinallyBlocks> 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();
@@ -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) && !"<clinit>".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;
}