@@ -25,6 +25,7 @@ import java.util.zip.GZIPOutputStream
25
25
import com.semmle.extractor.java.OdasaOutput
26
26
import com.semmle.extractor.java.OdasaOutput.TrapFileManager
27
27
import com.semmle.util.files.FileUtil
28
+ import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
28
29
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
29
30
import org.jetbrains.kotlin.ir.util.*
30
31
import org.jetbrains.kotlin.types.Variance
@@ -373,6 +374,8 @@ open class KotlinUsesExtractor(
373
374
d.origin == IrDeclarationOrigin .IR_EXTERNAL_JAVA_DECLARATION_STUB
374
375
}
375
376
377
+ fun isArray (t : IrSimpleType ) = t.isBoxedArray || t.isPrimitiveArray()
378
+
376
379
fun extractClassLaterIfExternal (c : IrClass ) {
377
380
if (isExternalDeclaration(c)) {
378
381
extractExternalClassLater(c)
@@ -411,7 +414,7 @@ open class KotlinUsesExtractor(
411
414
else
412
415
primitiveInfo.primitiveName
413
416
414
- type.isBoxedArray || type.isPrimitiveArray( ) -> {
417
+ isArray(type ) -> {
415
418
val elementType = type.getArrayElementType(pluginContext.irBuiltIns)
416
419
val javaElementType = if (type.isPrimitiveArray()) elementType else elementType.makeNullable()
417
420
shortName(javaElementType) + " []"
@@ -521,22 +524,52 @@ open class KotlinUsesExtractor(
521
524
return TypeResults (javaResult, kotlinResult)
522
525
}
523
526
524
- fun useArrayType (arrayType : IrSimpleType , componentType : IrType , elementType : IrType , dimensions : Int ): TypeResults {
527
+ // Given either a primitive array or a boxed array, returns primitive arrays unchanged,
528
+ // but returns boxed arrays with a nullable, invariant component type, with any nested arrays
529
+ // similarly transformed. For example, Array<out Array<in E>> would become Array<Array<E?>?>
530
+ // Array<*> will become Array<Any?>.
531
+ fun getInvariantNullableArrayType (arrayType : IrSimpleType ): IrSimpleType =
532
+ if (arrayType.isPrimitiveArray())
533
+ arrayType
534
+ else {
535
+ val componentType = arrayType.getArrayElementType(pluginContext.irBuiltIns)
536
+ val componentTypeBroadened = when (componentType) {
537
+ is IrSimpleType ->
538
+ if (isArray(componentType)) getInvariantNullableArrayType(componentType) else componentType
539
+ else -> componentType
540
+ }
541
+ val unchanged =
542
+ componentType == componentTypeBroadened &&
543
+ (arrayType.arguments[0 ] as ? IrTypeProjection )?.variance == Variance .INVARIANT &&
544
+ componentType.isNullable()
545
+ if (unchanged)
546
+ arrayType
547
+ else
548
+ IrSimpleTypeImpl (
549
+ arrayType.classifier,
550
+ true ,
551
+ listOf (makeTypeProjection(componentTypeBroadened, Variance .INVARIANT )),
552
+ listOf ()
553
+ )
554
+ }
555
+
556
+ fun useArrayType (arrayType : IrSimpleType , componentType : IrType , elementType : IrType , dimensions : Int , isPrimitiveArray : Boolean ): TypeResults {
557
+
558
+ // Ensure we extract Array<Int> as Integer[], not int[], for example:
559
+ fun nullableIfNotPrimitive (type : IrType ) = if (type.isPrimitiveType() && ! isPrimitiveArray) type.makeNullable() else type
525
560
526
561
// TODO: Figure out what signatures should be returned
527
562
528
- val componentTypeLabels = useType(componentType)
529
- val elementTypeLabels = useType(elementType)
563
+ val componentTypeLabel = useType(nullableIfNotPrimitive( componentType)).javaResult.id
564
+ val elementTypeLabel = useType(nullableIfNotPrimitive( elementType)).javaResult.id
530
565
531
- val id = tw.getLabelFor<DbArray >(" @\" array;$dimensions ;{${elementTypeLabels.javaResult.id } }\" " ) {
566
+ val id = tw.getLabelFor<DbArray >(" @\" array;$dimensions ;{${elementTypeLabel } }\" " ) {
532
567
tw.writeArrays(
533
568
it,
534
569
shortName(arrayType),
535
- elementTypeLabels.javaResult.id,
536
- elementTypeLabels.kotlinResult.id,
570
+ elementTypeLabel,
537
571
dimensions,
538
- componentTypeLabels.javaResult.id,
539
- componentTypeLabels.kotlinResult.id)
572
+ componentTypeLabel)
540
573
541
574
extractClassCommon(arrayType.classifier.owner as IrClass , it)
542
575
@@ -547,29 +580,23 @@ open class KotlinUsesExtractor(
547
580
// TODO: modifiers
548
581
// tw.writeHasModifier(length, getModifierKey("public"))
549
582
// tw.writeHasModifier(length, getModifierKey("final"))
550
- }
551
583
552
- val javaSignature = " an array" // TODO: Wrong
553
- val javaResult = TypeResult (id, javaSignature)
554
- // Note the stripping of any type projection from `componentType` here mirrors the action of `IrType.getArrayElementType`,
555
- // 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>)
556
- val owner: IrClass = arrayType.classifier.owner as IrClass
557
- val kotlinTypeArgs = if (arrayType.arguments.isEmpty()) listOf () else listOf (makeTypeProjection(componentType, Variance .INVARIANT ))
558
- val kotlinClassName = getUnquotedClassLabel(owner, kotlinTypeArgs)
559
- val kotlinSignature = " $javaSignature ?" // TODO: Wrong
560
- val kotlinLabel = " @\" kt_type;nullable;${kotlinClassName} \" "
561
- val kotlinId: Label <DbKt_nullable_type > = tw.getLabelFor(kotlinLabel, {
562
- tw.writeKt_nullable_types(it, id)
563
- })
564
- val kotlinResult = TypeResult (kotlinId, kotlinSignature)
584
+ // Note we will only emit one `clone()` method per Java array type, so we choose `Array<C?>` as its Kotlin
585
+ // return type, where C is the component type with any nested arrays themselves invariant and nullable.
586
+ val kotlinCloneReturnType = getInvariantNullableArrayType(arrayType).makeNullable()
587
+ val kotlinCloneReturnTypeLabel = useType(kotlinCloneReturnType).kotlinResult.id
565
588
566
- tw.getLabelFor<DbMethod >(" @\" callable;{$id }.clone(){$id }\" " ) {
567
- tw.writeMethods(it , " clone" , " clone()" , javaResult.id, kotlinResult.id, javaResult.id, it )
589
+ val clone = tw.getLabelFor<DbMethod >(" @\" callable;{$it }.clone(){$it }\" " )
590
+ tw.writeMethods(clone , " clone" , " clone()" , it, kotlinCloneReturnTypeLabel, it, clone )
568
591
// TODO: modifiers
569
592
// tw.writeHasModifier(clone, getModifierKey("public"))
570
593
}
571
594
572
- return TypeResults (javaResult, kotlinResult)
595
+ val javaSignature = " an array" // TODO: Wrong
596
+ val javaResult = TypeResult (id, javaSignature)
597
+
598
+ val arrayClassResult = useSimpleTypeClass(arrayType.classifier.owner as IrClass , arrayType.arguments, arrayType.hasQuestionMark)
599
+ return TypeResults (javaResult, arrayClassResult.kotlinResult)
573
600
}
574
601
575
602
fun useSimpleType (s : IrSimpleType , canReturnPrimitiveTypes : Boolean ): TypeResults {
@@ -670,13 +697,12 @@ class X {
670
697
elementType = elementType.getArrayElementType(pluginContext.irBuiltIns)
671
698
}
672
699
673
- fun nullableUnlessPrimitive (type : IrType ) = if (isPrimitiveArray && type.isPrimitiveType()) type else type.makeNullable()
674
-
675
700
return useArrayType(
676
701
s,
677
- nullableUnlessPrimitive(componentType),
678
- nullableUnlessPrimitive(elementType),
679
- dimensions
702
+ componentType,
703
+ elementType,
704
+ dimensions,
705
+ isPrimitiveArray
680
706
)
681
707
}
682
708
0 commit comments