Add null checks in constructors taking value class types
^KT-53492: Fixed
This commit is contained in:
committed by
teamcity
parent
cd6e865fb3
commit
50e92d2238
+6
@@ -32585,6 +32585,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/notNullAssertions/callAssertions.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("constructorWithMangledParams.kt")
|
||||
public void testConstructorWithMangledParams() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/notNullAssertions/constructorWithMangledParams.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("definitelyNotNullTypes.kt")
|
||||
public void testDefinitelyNotNullTypes() throws Exception {
|
||||
|
||||
+6
-4
@@ -322,10 +322,12 @@ class ExpressionCodegen(
|
||||
|
||||
// * Operator functions require non-null assertions on parameters even if they are private.
|
||||
// * Local function for lambda survives at this stage if it was used in 'invokedynamic'-based code.
|
||||
// Such functions require non-null assertions on parameters.
|
||||
private fun shouldGenerateNonNullAssertionsForPrivateFun(irFunction: IrFunction) =
|
||||
irFunction is IrSimpleFunction && irFunction.isOperator ||
|
||||
irFunction.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
|
||||
// * Hidden constructors with mangled parameters require non-null assertions (see KT-53492)
|
||||
private fun shouldGenerateNonNullAssertionsForPrivateFun(irFunction: IrFunction): Boolean {
|
||||
if (irFunction is IrSimpleFunction && irFunction.isOperator || irFunction.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) return true
|
||||
if (context.hiddenConstructorsWithMangledParams.containsKey(irFunction)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
private fun generateNonNullAssertion(param: IrValueParameter) {
|
||||
if (param.origin == JvmLoweredDeclarationOrigin.FIELD_FOR_OUTER_THIS ||
|
||||
|
||||
+55
-40
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
internal class SyntheticAccessorLowering(val context: JvmBackendContext) : FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
@@ -142,8 +143,10 @@ private class SyntheticAccessorTransformer(
|
||||
}
|
||||
|
||||
val accessor = when {
|
||||
callee is IrConstructor && callee.isOrShouldBeHidden ->
|
||||
handleHiddenConstructor(callee).symbol
|
||||
callee is IrConstructor && callee.isOrShouldBeHiddenAsSealedClassConstructor ->
|
||||
handleHiddenConstructorOfSealedClass(callee).symbol
|
||||
callee is IrConstructor && callee.isOrShouldBeHiddenSinceHasMangledParams ->
|
||||
handleHiddenConstructorWithMangledParams(callee).symbol
|
||||
!expression.symbol.isAccessible(withSuper, thisSymbol) ->
|
||||
createAccessor(expression)
|
||||
else ->
|
||||
@@ -331,59 +334,71 @@ private class SyntheticAccessorTransformer(
|
||||
}
|
||||
|
||||
override fun visitConstructor(declaration: IrConstructor): IrStatement {
|
||||
if (declaration.isOrShouldBeHidden) {
|
||||
pendingAccessorsToAdd.add(handleHiddenConstructor(declaration))
|
||||
declaration.visibility = DescriptorVisibilities.PRIVATE
|
||||
when {
|
||||
declaration.isOrShouldBeHiddenSinceHasMangledParams -> {
|
||||
pendingAccessorsToAdd.add(handleHiddenConstructorWithMangledParams(declaration))
|
||||
declaration.visibility = DescriptorVisibilities.PRIVATE
|
||||
}
|
||||
declaration.isOrShouldBeHiddenAsSealedClassConstructor -> {
|
||||
pendingAccessorsToAdd.add(handleHiddenConstructorOfSealedClass(declaration))
|
||||
declaration.visibility = DescriptorVisibilities.PRIVATE
|
||||
}
|
||||
}
|
||||
|
||||
return super.visitConstructor(declaration)
|
||||
}
|
||||
|
||||
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||
val function = expression.symbol.owner
|
||||
|
||||
if (!expression.origin.isLambda && function is IrConstructor && function.isOrShouldBeHidden) {
|
||||
handleHiddenConstructor(function).let { accessor ->
|
||||
expression.transformChildrenVoid()
|
||||
return IrFunctionReferenceImpl(
|
||||
expression.startOffset, expression.endOffset, expression.type,
|
||||
accessor.symbol, accessor.typeParameters.size,
|
||||
accessor.valueParameters.size, accessor.symbol, expression.origin
|
||||
)
|
||||
}
|
||||
if (!expression.origin.isLambda && function is IrConstructor
|
||||
&& (function.isOrShouldBeHiddenSinceHasMangledParams || function.isOrShouldBeHiddenAsSealedClassConstructor)
|
||||
) {
|
||||
val accessor =
|
||||
if (function.isOrShouldBeHiddenSinceHasMangledParams)
|
||||
handleHiddenConstructorWithMangledParams(function)
|
||||
else
|
||||
handleHiddenConstructorOfSealedClass(function)
|
||||
expression.transformChildrenVoid()
|
||||
return IrFunctionReferenceImpl(
|
||||
expression.startOffset, expression.endOffset, expression.type,
|
||||
accessor.symbol, accessor.typeParameters.size,
|
||||
accessor.valueParameters.size, accessor.symbol, expression.origin
|
||||
)
|
||||
}
|
||||
|
||||
return super.visitFunctionReference(expression)
|
||||
}
|
||||
|
||||
private val IrConstructor.isOrShouldBeHidden: Boolean
|
||||
private val IrConstructor.isOrShouldBeHiddenSinceHasMangledParams: Boolean
|
||||
get() {
|
||||
if (this in context.hiddenConstructors.keys)
|
||||
return true
|
||||
|
||||
if (origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
|
||||
origin == JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR ||
|
||||
origin == JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR_FOR_HIDDEN_CONSTRUCTOR ||
|
||||
origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
val constructedClass = constructedClass
|
||||
|
||||
if (!DescriptorVisibilities.isPrivate(visibility) && !constructedClass.isValue && hasMangledParameters() &&
|
||||
!constructedClass.isAnonymousObject
|
||||
) return true
|
||||
|
||||
if (visibility != DescriptorVisibilities.PUBLIC && constructedClass.modality == Modality.SEALED)
|
||||
return true
|
||||
|
||||
return false
|
||||
if (this in context.hiddenConstructorsWithMangledParams.keys) return true
|
||||
return isOrShouldBeHiddenDueToOrigin && !DescriptorVisibilities.isPrivate(visibility)
|
||||
&& !constructedClass.isValue && hasMangledParameters() && !constructedClass.isAnonymousObject
|
||||
}
|
||||
|
||||
private fun handleHiddenConstructor(declaration: IrConstructor): IrConstructor {
|
||||
require(declaration.isOrShouldBeHidden, declaration::render)
|
||||
return context.hiddenConstructors.getOrPut(declaration) {
|
||||
private val IrConstructor.isOrShouldBeHiddenAsSealedClassConstructor: Boolean
|
||||
get() {
|
||||
if (this in context.hiddenConstructorsOfSealedClasses.keys) return true
|
||||
return isOrShouldBeHiddenDueToOrigin && visibility != DescriptorVisibilities.PUBLIC && constructedClass.modality == Modality.SEALED
|
||||
}
|
||||
|
||||
private val IrConstructor.isOrShouldBeHiddenDueToOrigin: Boolean
|
||||
get() = !(origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
|
||||
origin == JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR ||
|
||||
origin == JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR_FOR_HIDDEN_CONSTRUCTOR ||
|
||||
origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB)
|
||||
|
||||
private fun handleHiddenConstructorWithMangledParams(declaration: IrConstructor) =
|
||||
handleHiddenConstructor(declaration, context.hiddenConstructorsWithMangledParams)
|
||||
|
||||
private fun handleHiddenConstructorOfSealedClass(declaration: IrConstructor) =
|
||||
handleHiddenConstructor(declaration, context.hiddenConstructorsOfSealedClasses)
|
||||
|
||||
private fun handleHiddenConstructor(
|
||||
declaration: IrConstructor,
|
||||
constructorToAccessorMap: ConcurrentHashMap<IrConstructor, IrConstructor>
|
||||
): IrConstructor {
|
||||
return constructorToAccessorMap.getOrPut(declaration) {
|
||||
declaration.makeConstructorAccessor(JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR_FOR_HIDDEN_CONSTRUCTOR).also { accessor ->
|
||||
if (declaration.constructedClass.modality != Modality.SEALED) {
|
||||
// There's a special case in the JVM backend for serializing the metadata of hidden
|
||||
|
||||
@@ -114,7 +114,8 @@ class JvmBackendContext(
|
||||
val multifileFacadeClassForPart = mutableMapOf<IrClass, IrClass>()
|
||||
val multifileFacadeMemberToPartMember = mutableMapOf<IrSimpleFunction, IrSimpleFunction>()
|
||||
|
||||
val hiddenConstructors = ConcurrentHashMap<IrConstructor, IrConstructor>()
|
||||
val hiddenConstructorsWithMangledParams = ConcurrentHashMap<IrConstructor, IrConstructor>()
|
||||
val hiddenConstructorsOfSealedClasses = ConcurrentHashMap<IrConstructor, IrConstructor>()
|
||||
|
||||
val collectionStubComputer = CollectionStubComputer(this)
|
||||
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// WITH_REFLECT
|
||||
// FULL_JDK
|
||||
// WORKS_WHEN_VALUE_CLASS
|
||||
// LANGUAGE: +ValueClasses
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
import java.lang.NullPointerException
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
|
||||
|
||||
OPTIONAL_JVM_INLINE_ANNOTATION
|
||||
value class IC(val str: String)
|
||||
|
||||
class A(val a: IC, val x : String) {
|
||||
fun foo() = "$a$x"
|
||||
|
||||
private constructor(x: IC) : this(IC(""), "")
|
||||
}
|
||||
|
||||
inline fun assertThrowsExpectedException(block: () -> Unit): Boolean {
|
||||
try {
|
||||
block()
|
||||
} catch (t: Throwable) {
|
||||
return t is InvocationTargetException && t.targetException is NullPointerException
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
if (!assertThrowsExpectedException { ::A.call(null, "").foo() }) return "Fail 1"
|
||||
if (!assertThrowsExpectedException { ::A.call(IC(""), null).foo() }) return "Fail 2"
|
||||
val privateConstructor = A::class.constructors.single { it.parameters.size == 1 }
|
||||
privateConstructor.also { it.isAccessible = true }.call(null).foo()
|
||||
return "OK"
|
||||
}
|
||||
+6
@@ -32585,6 +32585,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/notNullAssertions/callAssertions.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("constructorWithMangledParams.kt")
|
||||
public void testConstructorWithMangledParams() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/notNullAssertions/constructorWithMangledParams.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("definitelyNotNullTypes.kt")
|
||||
public void testDefinitelyNotNullTypes() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user