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
|
package org.jetbrains.kotlin.codegen
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.createCustomCopy
|
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.config.JVMAssertionsMode
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.isTopLevelInPackage
|
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.calls.tasks.TracingStrategy
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
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.Label
|
||||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
|
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
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
|
||||||
const val ASSERTIONS_DISABLED_FIELD_NAME = "\$assertionsDisabled"
|
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(
|
classBuilder.newField(
|
||||||
JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_STATIC or Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC, ASSERTIONS_DISABLED_FIELD_NAME,
|
JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_STATIC or Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC, ASSERTIONS_DISABLED_FIELD_NAME,
|
||||||
"Z", null, null
|
"Z", null, null
|
||||||
@@ -139,7 +144,7 @@ fun generateAssertionsDisabledFieldInitialization(classBuilder: ClassBuilder, cl
|
|||||||
val elseLabel = Label()
|
val elseLabel = Label()
|
||||||
with(InstructionAdapter(clInitBuilder)) {
|
with(InstructionAdapter(clInitBuilder)) {
|
||||||
mark(Label())
|
mark(Label())
|
||||||
aconst(Type.getObjectType(classBuilder.thisName))
|
aconst(Type.getObjectType(className))
|
||||||
invokevirtual("java/lang/Class", "desiredAssertionStatus", "()Z", false)
|
invokevirtual("java/lang/Class", "desiredAssertionStatus", "()Z", false)
|
||||||
ifne(thenLabel)
|
ifne(thenLabel)
|
||||||
iconst(1)
|
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> {
|
private fun <D : FunctionDescriptor> ResolvedCall<D>.replaceAssertWithAssertInner(): ResolvedCall<D> {
|
||||||
val newCandidateDescriptor = resultingDescriptor.createCustomCopy {
|
val newCandidateDescriptor = resultingDescriptor.createCustomCopy {
|
||||||
setName(Name.identifier(ALWAYS_ENABLED_ASSERT_FUNCTION_NAME))
|
setName(Name.identifier(ALWAYS_ENABLED_ASSERT_FUNCTION_NAME))
|
||||||
|
|||||||
@@ -965,7 +965,7 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
|||||||
|
|
||||||
public void generateAssertField() {
|
public void generateAssertField() {
|
||||||
if (jvmAssertFieldGenerated) return;
|
if (jvmAssertFieldGenerated) return;
|
||||||
AssertCodegenUtilKt.generateAssertionsDisabledFieldInitialization(v, createOrGetClInitCodegen().v);
|
AssertCodegenUtilKt.generateAssertionsDisabledFieldInitialization(v, createOrGetClInitCodegen().v, v.getThisName());
|
||||||
jvmAssertFieldGenerated = true;
|
jvmAssertFieldGenerated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -154,6 +154,11 @@ class AnonymousObjectTransformer(
|
|||||||
coroutineTransformer.shouldGenerateStateMachine(next) -> coroutineTransformer.newMethod(next)
|
coroutineTransformer.shouldGenerateStateMachine(next) -> coroutineTransformer.newMethod(next)
|
||||||
else -> newMethod(classBuilder, next)
|
else -> newMethod(classBuilder, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (next.name == "<clinit>") {
|
||||||
|
rewriteAssertionsDisabledFieldInitialization(next, inliningContext.root.callSiteInfo.ownerClassName)
|
||||||
|
}
|
||||||
|
|
||||||
val funResult = inlineMethodAndUpdateGlobalResult(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, false)
|
val funResult = inlineMethodAndUpdateGlobalResult(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, false)
|
||||||
|
|
||||||
val returnType = Type.getReturnType(next.desc)
|
val returnType = Type.getReturnType(next.desc)
|
||||||
@@ -195,7 +200,7 @@ class AnonymousObjectTransformer(
|
|||||||
|
|
||||||
if (inliningContext.generateAssertField && fieldNames.none { it.key == ASSERTIONS_DISABLED_FIELD_NAME }) {
|
if (inliningContext.generateAssertField && fieldNames.none { it.key == ASSERTIONS_DISABLED_FIELD_NAME }) {
|
||||||
val clInitBuilder = classBuilder.newMethod(NO_ORIGIN, Opcodes.ACC_STATIC, "<clinit>", "()V", null, null)
|
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.visitInsn(Opcodes.RETURN)
|
||||||
clInitBuilder.visitEnd()
|
clInitBuilder.visitEnd()
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-12
@@ -1,6 +1,4 @@
|
|||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
|
|
||||||
// FILE: inline.kt
|
// FILE: inline.kt
|
||||||
// KOTLIN_CONFIGURATION_FLAGS: ASSERTIONS_MODE=jvm
|
// KOTLIN_CONFIGURATION_FLAGS: ASSERTIONS_MODE=jvm
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
@@ -185,8 +183,9 @@ class ShouldBeEnabled : Checker {
|
|||||||
fun setDesiredAssertionStatus(v: Boolean): Checker {
|
fun setDesiredAssertionStatus(v: Boolean): Checker {
|
||||||
val loader = Checker::class.java.classLoader
|
val loader = Checker::class.java.classLoader
|
||||||
loader.setDefaultAssertionStatus(false)
|
loader.setDefaultAssertionStatus(false)
|
||||||
loader.setPackageAssertionStatus("test", v)
|
val className = if (v) "ShouldBeEnabled" else "ShouldBeDisabled"
|
||||||
val c = loader.loadClass(if (v) "ShouldBeEnabled" else "ShouldBeDisabled")
|
loader.setClassAssertionStatus(className, v)
|
||||||
|
val c = loader.loadClass(className)
|
||||||
return c.newInstance() as Checker
|
return c.newInstance() as Checker
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,14 +201,32 @@ fun box(): String {
|
|||||||
if (c.checkFalseWithMessageFalse()) return "FAIL 31"
|
if (c.checkFalseWithMessageFalse()) return "FAIL 31"
|
||||||
|
|
||||||
c = setDesiredAssertionStatus(true)
|
c = setDesiredAssertionStatus(true)
|
||||||
if (c.checkTrueTrue()) return "FAIL 100"
|
if (!c.checkTrueTrue()) return "FAIL 100"
|
||||||
if (c.checkTrueFalse()) return "FAIL 101"
|
try {
|
||||||
if (c.checkTrueWithMessageTrue()) return "FAIL 110"
|
c.checkTrueFalse()
|
||||||
if (c.checkTrueWithMessageFalse()) return "FAIL 111"
|
return "FAIL 101"
|
||||||
if (c.checkFalseTrue()) return "FAIL 120"
|
} catch (ignore: AssertionError) {}
|
||||||
if (c.checkFalseFalse()) return "FAIL 121"
|
if (!c.checkTrueWithMessageTrue()) return "FAIL 110"
|
||||||
if (c.checkFalseWithMessageTrue()) return "FAIL 130"
|
try {
|
||||||
if (c.checkFalseWithMessageFalse()) return "FAIL 131"
|
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"
|
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
|
// KOTLIN_CONFIGURATION_FLAGS: ASSERTIONS_MODE=jvm
|
||||||
|
|
||||||
inline fun inlineMe(crossinline c : () -> Unit) = {
|
inline fun inlineMe(crossinline c : () -> Unit) = {
|
||||||
|
|||||||
Reference in New Issue
Block a user