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:
committed by
Ilmir Usmanov
parent
272f6abe69
commit
ba90e87756
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -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()
|
||||
}
|
||||
|
||||
+29
-12
@@ -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) = {
|
||||
|
||||
Reference in New Issue
Block a user