@@ -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,11 @@ open class KotlinUsesExtractor(
411
414
else
412
415
primitiveInfo.primitiveName
413
416
414
- type.isBoxedArray || type.isPrimitiveArray() -> shortName(type.getArrayElementType(pluginContext.irBuiltIns)) + " []"
417
+ isArray(type) -> {
418
+ val elementType = type.getArrayElementType(pluginContext.irBuiltIns)
419
+ val javaElementType = if (type.isPrimitiveArray()) elementType else elementType.makeNullable()
420
+ shortName(javaElementType) + " []"
421
+ }
415
422
416
423
type.classifier.owner is IrClass -> {
417
424
val c = type.classifier.owner as IrClass
@@ -517,6 +524,81 @@ open class KotlinUsesExtractor(
517
524
return TypeResults (javaResult, kotlinResult)
518
525
}
519
526
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
560
+
561
+ // TODO: Figure out what signatures should be returned
562
+
563
+ val componentTypeLabel = useType(nullableIfNotPrimitive(componentType)).javaResult.id
564
+ val elementTypeLabel = useType(nullableIfNotPrimitive(elementType)).javaResult.id
565
+
566
+ val id = tw.getLabelFor<DbArray >(" @\" array;$dimensions ;{${elementTypeLabel} }\" " ) {
567
+ tw.writeArrays(
568
+ it,
569
+ shortName(arrayType),
570
+ elementTypeLabel,
571
+ dimensions,
572
+ componentTypeLabel)
573
+
574
+ extractClassCommon(arrayType.classifier.owner as IrClass , it)
575
+
576
+ // array.length
577
+ val length = tw.getLabelFor<DbField >(" @\" field;{$it };length\" " )
578
+ val intTypeIds = useType(pluginContext.irBuiltIns.intType)
579
+ tw.writeFields(length, " length" , intTypeIds.javaResult.id, intTypeIds.kotlinResult.id, it, length)
580
+ // TODO: modifiers
581
+ // tw.writeHasModifier(length, getModifierKey("public"))
582
+ // tw.writeHasModifier(length, getModifierKey("final"))
583
+
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
588
+
589
+ val clone = tw.getLabelFor<DbMethod >(" @\" callable;{$it }.clone(){$it }\" " )
590
+ tw.writeMethods(clone, " clone" , " clone()" , it, kotlinCloneReturnTypeLabel, it, clone)
591
+ // TODO: modifiers
592
+ // tw.writeHasModifier(clone, getModifierKey("public"))
593
+ }
594
+
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)
600
+ }
601
+
520
602
fun useSimpleType (s : IrSimpleType , canReturnPrimitiveTypes : Boolean ): TypeResults {
521
603
if (s.abbreviation != null ) {
522
604
// TODO: Extract this information
@@ -604,67 +686,24 @@ class X {
604
686
*/
605
687
606
688
(s.isBoxedArray && s.arguments.isNotEmpty()) || s.isPrimitiveArray() -> {
607
- // TODO: fix this, this is only a dummy implementation to let the tests pass
608
- // TODO: Figure out what signatures should be returned
609
- // TODO: Generate a short name for array types
610
-
611
689
var dimensions = 1
690
+ var isPrimitiveArray = s.isPrimitiveArray()
612
691
val componentType = s.getArrayElementType(pluginContext.irBuiltIns)
613
692
var elementType = componentType
614
693
while (elementType.isBoxedArray || elementType.isPrimitiveArray()) {
615
694
dimensions++
695
+ if (elementType.isPrimitiveArray())
696
+ isPrimitiveArray = true
616
697
elementType = elementType.getArrayElementType(pluginContext.irBuiltIns)
617
698
}
618
699
619
- val componentTypeLabel = useType(componentType)
620
- val elementTypeLabel = useType(elementType)
621
-
622
- fun kotlinLabelOfJavaType (type : IrType , typeLabel : Label <out DbKt_type >) =
623
- if (type.isPrimitiveType())
624
- // Java lowering distinguishes nullable and non-nullable, so keep the existing label
625
- typeLabel
626
- else
627
- // Java lowering always concerns the nullable type, so get the nullable equivalent
628
- useType(type.makeNullable()).kotlinResult.id
629
-
630
- val kotlinComponentTypeLabel = kotlinLabelOfJavaType(componentType, componentTypeLabel.kotlinResult.id)
631
- val kotlinElementTypeLabel = kotlinLabelOfJavaType(elementType, elementTypeLabel.kotlinResult.id)
632
-
633
- val id = tw.getLabelFor<DbArray >(" @\" array;$dimensions ;{${elementTypeLabel.javaResult.id} }\" " ) {
634
- tw.writeArrays(it, shortName(s), elementTypeLabel.javaResult.id, kotlinElementTypeLabel, dimensions, componentTypeLabel.javaResult.id, kotlinComponentTypeLabel)
635
-
636
- extractClassCommon(s.classifier.owner as IrClass , it)
637
-
638
- // array.length
639
- val length = tw.getLabelFor<DbField >(" @\" field;{$it };length\" " )
640
- val intTypeIds = useType(pluginContext.irBuiltIns.intType)
641
- tw.writeFields(length, " length" , intTypeIds.javaResult.id, intTypeIds.kotlinResult.id, it, length)
642
- // TODO: modifiers
643
- // tw.writeHasModifier(length, getModifierKey("public"))
644
- // tw.writeHasModifier(length, getModifierKey("final"))
645
- }
646
-
647
- val javaSignature = " an array" // TODO: Wrong
648
- val javaResult = TypeResult (id, javaSignature)
649
- val owner: IrClass = s.classifier.owner as IrClass
650
- val kotlinClassName = getUnquotedClassLabel(owner, listOf (makeTypeProjection(componentType, Variance .INVARIANT )))
651
- val kotlinSignature = " $javaSignature ?" // TODO: Wrong
652
- val kotlinLabel = " @\" kt_type;nullable;${kotlinClassName} \" "
653
- val kotlinId: Label <DbKt_nullable_type > = tw.getLabelFor(kotlinLabel, {
654
- tw.writeKt_nullable_types(it, id)
655
- })
656
- val kotlinResult = TypeResult (kotlinId, kotlinSignature)
657
-
658
- /*
659
- TODO
660
- tw.getLabelFor<DbMethod>("@\"callable;{$id}.clone(){$id}\"") {
661
- tw.writeMethods(it, "clone", "clone()", javaResult.id, kotlinResult.id, javaResult.id, it)
662
- // TODO: modifiers
663
- // tw.writeHasModifier(clone, getModifierKey("public"))
664
- }
665
- */
666
-
667
- return TypeResults (javaResult, kotlinResult)
700
+ return useArrayType(
701
+ s,
702
+ componentType,
703
+ elementType,
704
+ dimensions,
705
+ isPrimitiveArray
706
+ )
668
707
}
669
708
670
709
s.classifier.owner is IrClass -> {
0 commit comments