@@ -25,6 +25,7 @@ import java.util.zip.GZIPOutputStream
2525import com.semmle.extractor.java.OdasaOutput
2626import com.semmle.extractor.java.OdasaOutput.TrapFileManager
2727import com.semmle.util.files.FileUtil
28+ import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
2829import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
2930import org.jetbrains.kotlin.ir.util.*
3031import kotlin.system.exitProcess
@@ -372,6 +373,8 @@ open class KotlinUsesExtractor(
372373 d.origin == IrDeclarationOrigin .IR_EXTERNAL_JAVA_DECLARATION_STUB
373374 }
374375
376+ fun isArray (t : IrSimpleType ) = t.isBoxedArray || t.isPrimitiveArray()
377+
375378 fun extractClassLaterIfExternal (c : IrClass ) {
376379 if (isExternalDeclaration(c)) {
377380 extractExternalClassLater(c)
@@ -410,7 +413,7 @@ open class KotlinUsesExtractor(
410413 else
411414 primitiveInfo.primitiveName
412415
413- type.isBoxedArray || type.isPrimitiveArray( ) -> {
416+ isArray(type ) -> {
414417 val elementType = type.getArrayElementType(pluginContext.irBuiltIns)
415418 val javaElementType = if (type.isPrimitiveArray()) elementType else elementType.makeNullable()
416419 shortName(javaElementType) + " []"
@@ -520,22 +523,52 @@ open class KotlinUsesExtractor(
520523 return TypeResults (javaResult, kotlinResult)
521524 }
522525
523- fun useArrayType (arrayType : IrSimpleType , componentType : IrType , elementType : IrType , dimensions : Int ): TypeResults {
526+ // Given either a primitive array or a boxed array, returns primitive arrays unchanged,
527+ // but returns boxed arrays with a nullable, invariant component type, with any nested arrays
528+ // similarly transformed. For example, Array<out Array<in E>> would become Array<Array<E?>?>
529+ // Array<*> will become Array<Any?>.
530+ fun getInvariantNullableArrayType (arrayType : IrSimpleType ): IrSimpleType =
531+ if (arrayType.isPrimitiveArray())
532+ arrayType
533+ else {
534+ val componentType = arrayType.getArrayElementType(pluginContext.irBuiltIns)
535+ val componentTypeBroadened = when (componentType) {
536+ is IrSimpleType ->
537+ if (isArray(componentType)) getInvariantNullableArrayType(componentType) else componentType
538+ else -> componentType
539+ }
540+ val unchanged =
541+ componentType == componentTypeBroadened &&
542+ (arrayType.arguments[0 ] as ? IrTypeProjection )?.variance == Variance .INVARIANT &&
543+ componentType.isNullable()
544+ if (unchanged)
545+ arrayType
546+ else
547+ IrSimpleTypeImpl (
548+ arrayType.classifier,
549+ true ,
550+ listOf (makeTypeProjection(componentTypeBroadened, Variance .INVARIANT )),
551+ listOf ()
552+ )
553+ }
554+
555+ fun useArrayType (arrayType : IrSimpleType , componentType : IrType , elementType : IrType , dimensions : Int , isPrimitiveArray : Boolean ): TypeResults {
556+
557+ // Ensure we extract Array<Int> as Integer[], not int[], for example:
558+ fun nullableIfNotPrimitive (type : IrType ) = if (type.isPrimitiveType() && ! isPrimitiveArray) type.makeNullable() else type
524559
525560 // TODO: Figure out what signatures should be returned
526561
527- val componentTypeLabels = useType(componentType)
528- val elementTypeLabels = useType(elementType)
562+ val componentTypeLabel = useType(nullableIfNotPrimitive( componentType)).javaResult.id
563+ val elementTypeLabel = useType(nullableIfNotPrimitive( elementType)).javaResult.id
529564
530- val id = tw.getLabelFor<DbArray >(" @\" array;$dimensions ;{${elementTypeLabels.javaResult.id } }\" " ) {
565+ val id = tw.getLabelFor<DbArray >(" @\" array;$dimensions ;{${elementTypeLabel } }\" " ) {
531566 tw.writeArrays(
532567 it,
533568 shortName(arrayType),
534- elementTypeLabels.javaResult.id,
535- elementTypeLabels.kotlinResult.id,
569+ elementTypeLabel,
536570 dimensions,
537- componentTypeLabels.javaResult.id,
538- componentTypeLabels.kotlinResult.id)
571+ componentTypeLabel)
539572
540573 extractClassCommon(arrayType.classifier.owner as IrClass , it)
541574
@@ -546,29 +579,23 @@ open class KotlinUsesExtractor(
546579 // TODO: modifiers
547580 // tw.writeHasModifier(length, getModifierKey("public"))
548581 // tw.writeHasModifier(length, getModifierKey("final"))
549- }
550582
551- val javaSignature = " an array" // TODO: Wrong
552- val javaResult = TypeResult (id, javaSignature)
553- // Note the stripping of any type projection from `componentType` here mirrors the action of `IrType.getArrayElementType`,
554- // and is required if we are not to produce different kotlin types for the same Java type (e.g. List[] -> Array<out List> or Array<List>)
555- val owner: IrClass = arrayType.classifier.owner as IrClass
556- val kotlinTypeArgs = if (arrayType.arguments.isEmpty()) listOf () else listOf (makeTypeProjection(componentType, Variance .INVARIANT ))
557- val kotlinClassName = getUnquotedClassLabel(owner, kotlinTypeArgs)
558- val kotlinSignature = " $javaSignature ?" // TODO: Wrong
559- val kotlinLabel = " @\" kt_type;nullable;${kotlinClassName} \" "
560- val kotlinId: Label <DbKt_nullable_type > = tw.getLabelFor(kotlinLabel, {
561- tw.writeKt_nullable_types(it, id)
562- })
563- val kotlinResult = TypeResult (kotlinId, kotlinSignature)
583+ // Note we will only emit one `clone()` method per Java array type, so we choose `Array<C?>` as its Kotlin
584+ // return type, where C is the component type with any nested arrays themselves invariant and nullable.
585+ val kotlinCloneReturnType = getInvariantNullableArrayType(arrayType).makeNullable()
586+ val kotlinCloneReturnTypeLabel = useType(kotlinCloneReturnType).kotlinResult.id
564587
565- tw.getLabelFor<DbMethod >(" @\" callable;{$id }.clone(){$id }\" " ) {
566- tw.writeMethods(it , " clone" , " clone()" , javaResult.id, kotlinResult.id, javaResult.id, it )
588+ val clone = tw.getLabelFor<DbMethod >(" @\" callable;{$it }.clone(){$it }\" " )
589+ tw.writeMethods(clone , " clone" , " clone()" , it, kotlinCloneReturnTypeLabel, it, clone )
567590 // TODO: modifiers
568591 // tw.writeHasModifier(clone, getModifierKey("public"))
569592 }
570593
571- return TypeResults (javaResult, kotlinResult)
594+ val javaSignature = " an array" // TODO: Wrong
595+ val javaResult = TypeResult (id, javaSignature)
596+
597+ val arrayClassResult = useSimpleTypeClass(arrayType.classifier.owner as IrClass , arrayType.arguments, arrayType.hasQuestionMark)
598+ return TypeResults (javaResult, arrayClassResult.kotlinResult)
572599 }
573600
574601 fun useSimpleType (s : IrSimpleType , canReturnPrimitiveTypes : Boolean ): TypeResults {
@@ -669,13 +696,12 @@ class X {
669696 elementType = elementType.getArrayElementType(pluginContext.irBuiltIns)
670697 }
671698
672- fun nullableUnlessPrimitive (type : IrType ) = if (isPrimitiveArray && type.isPrimitiveType()) type else type.makeNullable()
673-
674699 return useArrayType(
675700 s,
676- nullableUnlessPrimitive(componentType),
677- nullableUnlessPrimitive(elementType),
678- dimensions
701+ componentType,
702+ elementType,
703+ dimensions,
704+ isPrimitiveArray
679705 )
680706 }
681707
0 commit comments