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");
|
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
|
@Test
|
||||||
@TestMetadata("definitelyNotNullTypes.kt")
|
@TestMetadata("definitelyNotNullTypes.kt")
|
||||||
public void testDefinitelyNotNullTypes() throws Exception {
|
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.
|
// * 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.
|
// * Local function for lambda survives at this stage if it was used in 'invokedynamic'-based code.
|
||||||
// Such functions require non-null assertions on parameters.
|
// * Hidden constructors with mangled parameters require non-null assertions (see KT-53492)
|
||||||
private fun shouldGenerateNonNullAssertionsForPrivateFun(irFunction: IrFunction) =
|
private fun shouldGenerateNonNullAssertionsForPrivateFun(irFunction: IrFunction): Boolean {
|
||||||
irFunction is IrSimpleFunction && irFunction.isOperator ||
|
if (irFunction is IrSimpleFunction && irFunction.isOperator || irFunction.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) return true
|
||||||
irFunction.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
|
if (context.hiddenConstructorsWithMangledParams.containsKey(irFunction)) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
private fun generateNonNullAssertion(param: IrValueParameter) {
|
private fun generateNonNullAssertion(param: IrValueParameter) {
|
||||||
if (param.origin == JvmLoweredDeclarationOrigin.FIELD_FOR_OUTER_THIS ||
|
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.load.java.JvmAbi
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
internal class SyntheticAccessorLowering(val context: JvmBackendContext) : FileLoweringPass {
|
internal class SyntheticAccessorLowering(val context: JvmBackendContext) : FileLoweringPass {
|
||||||
override fun lower(irFile: IrFile) {
|
override fun lower(irFile: IrFile) {
|
||||||
@@ -142,8 +143,10 @@ private class SyntheticAccessorTransformer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val accessor = when {
|
val accessor = when {
|
||||||
callee is IrConstructor && callee.isOrShouldBeHidden ->
|
callee is IrConstructor && callee.isOrShouldBeHiddenAsSealedClassConstructor ->
|
||||||
handleHiddenConstructor(callee).symbol
|
handleHiddenConstructorOfSealedClass(callee).symbol
|
||||||
|
callee is IrConstructor && callee.isOrShouldBeHiddenSinceHasMangledParams ->
|
||||||
|
handleHiddenConstructorWithMangledParams(callee).symbol
|
||||||
!expression.symbol.isAccessible(withSuper, thisSymbol) ->
|
!expression.symbol.isAccessible(withSuper, thisSymbol) ->
|
||||||
createAccessor(expression)
|
createAccessor(expression)
|
||||||
else ->
|
else ->
|
||||||
@@ -331,59 +334,71 @@ private class SyntheticAccessorTransformer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitConstructor(declaration: IrConstructor): IrStatement {
|
override fun visitConstructor(declaration: IrConstructor): IrStatement {
|
||||||
if (declaration.isOrShouldBeHidden) {
|
when {
|
||||||
pendingAccessorsToAdd.add(handleHiddenConstructor(declaration))
|
declaration.isOrShouldBeHiddenSinceHasMangledParams -> {
|
||||||
declaration.visibility = DescriptorVisibilities.PRIVATE
|
pendingAccessorsToAdd.add(handleHiddenConstructorWithMangledParams(declaration))
|
||||||
|
declaration.visibility = DescriptorVisibilities.PRIVATE
|
||||||
|
}
|
||||||
|
declaration.isOrShouldBeHiddenAsSealedClassConstructor -> {
|
||||||
|
pendingAccessorsToAdd.add(handleHiddenConstructorOfSealedClass(declaration))
|
||||||
|
declaration.visibility = DescriptorVisibilities.PRIVATE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.visitConstructor(declaration)
|
return super.visitConstructor(declaration)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||||
val function = expression.symbol.owner
|
val function = expression.symbol.owner
|
||||||
|
|
||||||
if (!expression.origin.isLambda && function is IrConstructor && function.isOrShouldBeHidden) {
|
if (!expression.origin.isLambda && function is IrConstructor
|
||||||
handleHiddenConstructor(function).let { accessor ->
|
&& (function.isOrShouldBeHiddenSinceHasMangledParams || function.isOrShouldBeHiddenAsSealedClassConstructor)
|
||||||
expression.transformChildrenVoid()
|
) {
|
||||||
return IrFunctionReferenceImpl(
|
val accessor =
|
||||||
expression.startOffset, expression.endOffset, expression.type,
|
if (function.isOrShouldBeHiddenSinceHasMangledParams)
|
||||||
accessor.symbol, accessor.typeParameters.size,
|
handleHiddenConstructorWithMangledParams(function)
|
||||||
accessor.valueParameters.size, accessor.symbol, expression.origin
|
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)
|
return super.visitFunctionReference(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val IrConstructor.isOrShouldBeHidden: Boolean
|
private val IrConstructor.isOrShouldBeHiddenSinceHasMangledParams: Boolean
|
||||||
get() {
|
get() {
|
||||||
if (this in context.hiddenConstructors.keys)
|
if (this in context.hiddenConstructorsWithMangledParams.keys) return true
|
||||||
return true
|
return isOrShouldBeHiddenDueToOrigin && !DescriptorVisibilities.isPrivate(visibility)
|
||||||
|
&& !constructedClass.isValue && hasMangledParameters() && !constructedClass.isAnonymousObject
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleHiddenConstructor(declaration: IrConstructor): IrConstructor {
|
private val IrConstructor.isOrShouldBeHiddenAsSealedClassConstructor: Boolean
|
||||||
require(declaration.isOrShouldBeHidden, declaration::render)
|
get() {
|
||||||
return context.hiddenConstructors.getOrPut(declaration) {
|
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 ->
|
declaration.makeConstructorAccessor(JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR_FOR_HIDDEN_CONSTRUCTOR).also { accessor ->
|
||||||
if (declaration.constructedClass.modality != Modality.SEALED) {
|
if (declaration.constructedClass.modality != Modality.SEALED) {
|
||||||
// There's a special case in the JVM backend for serializing the metadata of hidden
|
// 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 multifileFacadeClassForPart = mutableMapOf<IrClass, IrClass>()
|
||||||
val multifileFacadeMemberToPartMember = mutableMapOf<IrSimpleFunction, IrSimpleFunction>()
|
val multifileFacadeMemberToPartMember = mutableMapOf<IrSimpleFunction, IrSimpleFunction>()
|
||||||
|
|
||||||
val hiddenConstructors = ConcurrentHashMap<IrConstructor, IrConstructor>()
|
val hiddenConstructorsWithMangledParams = ConcurrentHashMap<IrConstructor, IrConstructor>()
|
||||||
|
val hiddenConstructorsOfSealedClasses = ConcurrentHashMap<IrConstructor, IrConstructor>()
|
||||||
|
|
||||||
val collectionStubComputer = CollectionStubComputer(this)
|
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");
|
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
|
@Test
|
||||||
@TestMetadata("definitelyNotNullTypes.kt")
|
@TestMetadata("definitelyNotNullTypes.kt")
|
||||||
public void testDefinitelyNotNullTypes() throws Exception {
|
public void testDefinitelyNotNullTypes() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user