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 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;
} }
@@ -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()
} }
@@ -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) = {