Report error on state in multi-file class with -Xmultifile-parts-inherit

Simplify MultifileClassPartCodegen, remove related tests and change some
tests to use const val instead of val because backing fields for const
properties are stored in the facade, not parts

 #KT-23701 Fixed
This commit is contained in:
Alexander Udalov
2019-01-23 13:25:28 +01:00
parent 6b5a16884c
commit a2f4efbc2a
22 changed files with 88 additions and 474 deletions
@@ -659,8 +659,6 @@ public class FunctionCodegen {
genNotNullAssertionsForParameters(new InstructionAdapter(mv), parentCodegen.state, functionDescriptor, frameMap);
}
parentCodegen.beforeMethodBody(mv);
methodEnd = new Label();
context.setMethodEndLabel(methodEnd);
strategy.generateBody(mv, frameMap, signature, context, parentCodegen);
@@ -455,17 +455,13 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
if (clInit == null) {
DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
SimpleFunctionDescriptorImpl clInitDescriptor = createClInitFunctionDescriptor(contextDescriptor);
MethodVisitor mv = createClInitMethodVisitor(contextDescriptor);
MethodVisitor mv =
v.newMethod(JvmDeclarationOriginKt.OtherOrigin(contextDescriptor), ACC_STATIC, "<clinit>", "()V", null, null);
clInit = new ExpressionCodegen(mv, new FrameMap(), Type.VOID_TYPE, context.intoFunction(clInitDescriptor), state, this);
}
return clInit;
}
@NotNull
public MethodVisitor createClInitMethodVisitor(@NotNull DeclarationDescriptor contextDescriptor) {
return v.newMethod(JvmDeclarationOriginKt.OtherOrigin(contextDescriptor), ACC_STATIC, "<clinit>", "()V", null, null);
}
@NotNull
private SimpleFunctionDescriptorImpl createClInitFunctionDescriptor(@NotNull DeclarationDescriptor descriptor) {
SimpleFunctionDescriptorImpl clInit = SimpleFunctionDescriptorImpl.create(descriptor, Annotations.Companion.getEMPTY(),
@@ -496,9 +492,6 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
}
}
public void beforeMethodBody(@NotNull MethodVisitor mv) {
}
// Requires public access, because it is used by serialization plugin to generate initializer in synthetic constructor
public void initializeProperty(@NotNull ExpressionCodegen codegen, @NotNull KtProperty property) {
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(VARIABLE, property);
@@ -20,7 +20,6 @@ import com.intellij.util.ArrayUtil
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.codegen.context.MultifileClassPartContext
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
@@ -28,13 +27,8 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtTypeAlias
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
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
class MultifileClassPartCodegen(
v: ClassBuilder,
@@ -47,31 +41,13 @@ class MultifileClassPartCodegen(
) : 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, name, "()V", null, null)
private val staticInitClassBuilder = ClassBuilderOnDemand {
state.factory.newVisitor(MultifileClass(file, packageFragment), staticInitClassType, file).apply {
defineClass(file, state.classFileVersion, STATE_INITIALIZER_CLASS_ATTRIBUTES,
staticInitClassType.internalName, null, "java/lang/Object", ArrayUtil.EMPTY_STRING_ARRAY)
visitSource(file.name, null)
init {
if (shouldGeneratePartHierarchy && file.declarations.any { it is KtProperty && shouldInitializeProperty(it) }) {
throw AssertionError("State is not allowed in multi-file classes with -Xmultifile-parts-inherit")
}
}
private val requiresDeferredStaticInitialization =
shouldGeneratePartHierarchy && file.declarations.any {
it is KtProperty && shouldInitializeProperty(it)
}
override fun generate() {
if (!state.classBuilderMode.generateMultiFileFacadePartClasses) return
@@ -91,41 +67,15 @@ class MultifileClassPartCodegen(
visitEnd()
}
}
if (requiresDeferredStaticInitialization) {
staticInitClassBuilder.apply {
newField(OtherOrigin(packageFragment), Opcodes.ACC_STATIC or Opcodes.ACC_PRIVATE or Opcodes.ACC_VOLATILE,
CLINIT_SYNC_NAME, "I", null, null)
newSpecialMethod(packageFragment, CLINIT_TRIGGER_NAME).apply {
if (generateBodies) {
visitCode()
visitFieldInsn(Opcodes.GETSTATIC, staticInitClassType.internalName, CLINIT_SYNC_NAME, "I")
visitInsn(Opcodes.RETURN)
visitMaxs(1, 0)
}
visitEnd()
}
newSpecialMethod(packageFragment, "<clinit>").apply {
if (generateBodies) {
visitCode()
visitMethodInsn(Opcodes.INVOKESTATIC, partType.internalName, DEFERRED_PART_CLINIT_NAME, "()V", false)
visitInsn(Opcodes.ICONST_0)
visitFieldInsn(Opcodes.PUTSTATIC, staticInitClassType.internalName, CLINIT_SYNC_NAME, "I")
visitInsn(Opcodes.RETURN)
visitMaxs(1, 0)
}
visitEnd()
}
writeSyntheticClassMetadata(this, state)
}
}
}
override fun generateDeclaration() {
v.defineClass(element, state.classFileVersion, partClassAttributes, partType.internalName, null, superClassInternalName, ArrayUtil.EMPTY_STRING_ARRAY)
val access = if (shouldGeneratePartHierarchy) 0 else Opcodes.ACC_SYNTHETIC or Opcodes.ACC_FINAL
v.defineClass(
element, state.classFileVersion, access or Opcodes.ACC_SUPER, partType.internalName, null, superClassInternalName,
ArrayUtil.EMPTY_STRING_ARRAY
)
v.visitSource(element.name, null)
generatePropertyMetadataArrayFieldIfNeeded(partType)
@@ -143,20 +93,6 @@ 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 (serializer, packageProto) = PackagePartCodegen.serializePackagePartMembers(this, partType)
@@ -171,28 +107,4 @@ class MultifileClassPartCodegen(
override fun generateSyntheticPartsAfterBody() {
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 CLINIT_SYNC_NAME = "\$\$clinitSync"
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"
}
}
@@ -287,7 +287,6 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
sourceCompiler.generateAndInsertFinallyBlocks(
adapter, infos, (remapper.remap(parameters.argsSizeOnStack + 1).value as StackValue.Local).index
)
removeStaticInitializationTrigger(adapter)
if (!sourceCompiler.isFinallyMarkerRequired()) {
removeFinallyMarkers(adapter)
}
@@ -588,21 +587,6 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
return (name == "arrayOf" || name == "emptyArray") && callableDescriptor.containingDeclaration is BuiltInsPackageFragment
}
private fun removeStaticInitializationTrigger(methodNode: MethodNode) {
val insnList = methodNode.instructions
var insn: AbstractInsnNode? = insnList.first
while (insn != null) {
if (MultifileClassPartCodegen.isStaticInitTrigger(insn)) {
val clinitTriggerCall = insn
insn = insn.next
insnList.remove(clinitTriggerCall)
} else {
insn = insn.next
}
}
}
/*descriptor is null for captured vars*/
private fun shouldPutGeneralValue(type: Type, kotlinType: KotlinType?, stackValue: StackValue): Boolean {
//remap only inline functions (and maybe non primitives)