JVM, JVM IR: Fix assertion status for regenerated anonymous objects

We always set the $assertionsDisabled field based on the top-level
enclosing class. This means that for anonymous objects we have to
rewrite the call to Class.desiredAssertionStatus.
This commit is contained in:
Steven Schäfer
2020-02-14 11:09:32 +01:00
committed by Ilmir Usmanov
parent 272f6abe69
commit ba90e87756
5 changed files with 53 additions and 19 deletions
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.coroutines.createCustomCopy
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.common.findPreviousOrNull
import org.jetbrains.kotlin.config.JVMAssertionsMode
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.isTopLevelInPackage
@@ -19,12 +21,15 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.org.objectweb.asm.Label
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.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
import org.jetbrains.org.objectweb.asm.tree.LdcInsnNode
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
const val ASSERTIONS_DISABLED_FIELD_NAME = "\$assertionsDisabled"
@@ -130,7 +135,7 @@ private fun inlineAlwaysInlineAssert(resolvedCall: ResolvedCall<*>, codegen: Exp
)
}
fun generateAssertionsDisabledFieldInitialization(classBuilder: ClassBuilder, clInitBuilder: MethodVisitor) {
fun generateAssertionsDisabledFieldInitialization(classBuilder: ClassBuilder, clInitBuilder: MethodVisitor, className: String) {
classBuilder.newField(
JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_STATIC or Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC, ASSERTIONS_DISABLED_FIELD_NAME,
"Z", null, null
@@ -139,7 +144,7 @@ fun generateAssertionsDisabledFieldInitialization(classBuilder: ClassBuilder, cl
val elseLabel = Label()
with(InstructionAdapter(clInitBuilder)) {
mark(Label())
aconst(Type.getObjectType(classBuilder.thisName))
aconst(Type.getObjectType(className))
invokevirtual("java/lang/Class", "desiredAssertionStatus", "()Z", false)
ifne(thenLabel)
iconst(1)
@@ -153,6 +158,16 @@ fun generateAssertionsDisabledFieldInitialization(classBuilder: ClassBuilder, cl
}
}
fun rewriteAssertionsDisabledFieldInitialization(methodNode: MethodNode, className: String) {
InsnSequence(methodNode.instructions).firstOrNull {
it is FieldInsnNode && it.opcode == Opcodes.PUTSTATIC && it.name == ASSERTIONS_DISABLED_FIELD_NAME
}?.findPreviousOrNull {
it is MethodInsnNode && it.opcode == Opcodes.INVOKEVIRTUAL
&& it.owner == "java/lang/Class" && it.name == "desiredAssertionStatus" && it.desc == "()Z"
}?.previous?.safeAs<LdcInsnNode>()?.cst =
Type.getObjectType(className)
}
private fun <D : FunctionDescriptor> ResolvedCall<D>.replaceAssertWithAssertInner(): ResolvedCall<D> {
val newCandidateDescriptor = resultingDescriptor.createCustomCopy {
setName(Name.identifier(ALWAYS_ENABLED_ASSERT_FUNCTION_NAME))
@@ -965,7 +965,7 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
public void generateAssertField() {
if (jvmAssertFieldGenerated) return;
AssertCodegenUtilKt.generateAssertionsDisabledFieldInitialization(v, createOrGetClInitCodegen().v);
AssertCodegenUtilKt.generateAssertionsDisabledFieldInitialization(v, createOrGetClInitCodegen().v, v.getThisName());
jvmAssertFieldGenerated = true;
}
@@ -154,6 +154,11 @@ class AnonymousObjectTransformer(
coroutineTransformer.shouldGenerateStateMachine(next) -> coroutineTransformer.newMethod(next)
else -> newMethod(classBuilder, next)
}
if (next.name == "<clinit>") {
rewriteAssertionsDisabledFieldInitialization(next, inliningContext.root.callSiteInfo.ownerClassName)
}
val funResult = inlineMethodAndUpdateGlobalResult(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, false)
val returnType = Type.getReturnType(next.desc)
@@ -195,7 +200,7 @@ class AnonymousObjectTransformer(
if (inliningContext.generateAssertField && fieldNames.none { it.key == ASSERTIONS_DISABLED_FIELD_NAME }) {
val clInitBuilder = classBuilder.newMethod(NO_ORIGIN, Opcodes.ACC_STATIC, "<clinit>", "()V", null, null)
generateAssertionsDisabledFieldInitialization(classBuilder, clInitBuilder)
generateAssertionsDisabledFieldInitialization(classBuilder, clInitBuilder, inliningContext.root.callSiteInfo.ownerClassName)
clInitBuilder.visitInsn(Opcodes.RETURN)
clInitBuilder.visitEnd()
}
@@ -1,6 +1,4 @@
// TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: inline.kt
// KOTLIN_CONFIGURATION_FLAGS: ASSERTIONS_MODE=jvm
// WITH_RUNTIME
@@ -185,8 +183,9 @@ class ShouldBeEnabled : Checker {
fun setDesiredAssertionStatus(v: Boolean): Checker {
val loader = Checker::class.java.classLoader
loader.setDefaultAssertionStatus(false)
loader.setPackageAssertionStatus("test", v)
val c = loader.loadClass(if (v) "ShouldBeEnabled" else "ShouldBeDisabled")
val className = if (v) "ShouldBeEnabled" else "ShouldBeDisabled"
loader.setClassAssertionStatus(className, v)
val c = loader.loadClass(className)
return c.newInstance() as Checker
}
@@ -202,14 +201,32 @@ fun box(): String {
if (c.checkFalseWithMessageFalse()) return "FAIL 31"
c = setDesiredAssertionStatus(true)
if (c.checkTrueTrue()) return "FAIL 100"
if (c.checkTrueFalse()) return "FAIL 101"
if (c.checkTrueWithMessageTrue()) return "FAIL 110"
if (c.checkTrueWithMessageFalse()) return "FAIL 111"
if (c.checkFalseTrue()) return "FAIL 120"
if (c.checkFalseFalse()) return "FAIL 121"
if (c.checkFalseWithMessageTrue()) return "FAIL 130"
if (c.checkFalseWithMessageFalse()) return "FAIL 131"
if (!c.checkTrueTrue()) return "FAIL 100"
try {
c.checkTrueFalse()
return "FAIL 101"
} catch (ignore: AssertionError) {}
if (!c.checkTrueWithMessageTrue()) return "FAIL 110"
try {
c.checkTrueWithMessageFalse()
return "FAIL 111"
} catch (ignore: AssertionError) {}
try {
c.checkFalseTrue()
return "FAIL 120"
} catch (ignore: AssertionError) {}
try {
c.checkFalseFalse()
return "FAIL 121"
} catch (ignore: AssertionError) {}
try {
c.checkFalseWithMessageTrue()
return "FAIL 130"
} catch (ignore: AssertionError) {}
try {
c.checkFalseWithMessageFalse()
return "FAIL 131"
} catch (ignore: AssertionError) {}
return "OK"
}
@@ -1,6 +1,3 @@
// This test is ignored until KT-36794 is fixed
// IGNORE_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// KOTLIN_CONFIGURATION_FLAGS: ASSERTIONS_MODE=jvm
inline fun inlineMe(crossinline c : () -> Unit) = {