Skip to content

Commit 1b7e33b

Browse files
smowtonigfoo
authored andcommitted
Remove Kotlin element and component type from arrays
Now that these are no longer required, array extraction can extract kt-types consistently with other parameterised classes.
1 parent 70294bd commit 1b7e33b

6 files changed

Lines changed: 70 additions & 60 deletions

File tree

java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import java.util.zip.GZIPOutputStream
2525
import com.semmle.extractor.java.OdasaOutput
2626
import com.semmle.extractor.java.OdasaOutput.TrapFileManager
2727
import com.semmle.util.files.FileUtil
28+
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
2829
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
2930
import org.jetbrains.kotlin.ir.util.*
3031
import 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

java/ql/lib/config/semmlecode.dbscheme

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -512,10 +512,8 @@ arrays(
512512
unique int id: @array,
513513
string nodeName: string ref,
514514
int elementtypeid: @type ref,
515-
int elementkttypeid: @kt_type ref,
516515
int dimension: int ref,
517-
int componenttypeid: @type ref,
518-
int componentkttypeid: @kt_type ref
516+
int componenttypeid: @type ref
519517
);
520518

521519
enclInReftype(

java/ql/lib/semmle/code/Location.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ predicate hasName(Element e, string name) {
4040
or
4141
wildcards(e, name, _)
4242
or
43-
arrays(e, name, _, _, _, _, _)
43+
arrays(e, name, _, _, _)
4444
or
4545
modifiers(e, name)
4646
or

java/ql/lib/semmle/code/java/Type.qll

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -354,35 +354,21 @@ class Array extends RefType, @array {
354354
*
355355
* For example, the component type of `Object[][]` is `Object[]`.
356356
*/
357-
Type getComponentType() { arrays(this, _, _, _, _, result, _) }
357+
Type getComponentType() { arrays(this, _, _, _, result) }
358358

359359
/**
360-
* Gets the type of the components of this array type.
361-
*
362-
* For example, the component type of `Object[][]` is `Object[]`.
363-
*/
364-
KotlinType getComponentKotlinType() { arrays(this, _, _, _, _, _, result) }
365-
366-
/**
367-
* Gets the Kotlin type of the elements used to construct this array type.
368-
*
369-
* For example, the element type of `Object[][]` is `Object`.
370-
*/
371-
Type getElementType() { arrays(this, _, result, _, _, _, _) }
372-
373-
/**
374-
* Gets the Kotlin type of the elements used to construct this array type.
360+
* Gets the type of the elements used to construct this array type.
375361
*
376362
* For example, the element type of `Object[][]` is `Object`.
377363
*/
378-
KotlinType getElementKotlinType() { arrays(this, _, _, result, _, _, _) }
364+
Type getElementType() { arrays(this, _, result, _, _) }
379365

380366
/**
381367
* Gets the arity of this array type.
382368
*
383369
* For example, the dimension of `Object[][]` is 2.
384370
*/
385-
int getDimension() { arrays(this, _, _, _, result, _, _) }
371+
int getDimension() { arrays(this, _, _, result, _) }
386372

387373
/**
388374
* Gets the JVM descriptor for this type, as used in bytecode.
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
| primitiveArrays.kt:5:12:5:24 | a | file://:0:0:0:0 | Integer[] | file://:0:0:0:0 | Integer | file://:0:0:0:0 | Integer | file://:0:0:0:0 | Kotlin nullable Integer | file://:0:0:0:0 | Kotlin nullable Integer |
2-
| primitiveArrays.kt:5:27:5:40 | b | file://:0:0:0:0 | Integer[] | file://:0:0:0:0 | Integer | file://:0:0:0:0 | Integer | file://:0:0:0:0 | Kotlin nullable Integer | file://:0:0:0:0 | Kotlin nullable Integer |
3-
| primitiveArrays.kt:5:43:5:53 | c | file://:0:0:0:0 | int[] | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | Kotlin not-null Integer | file://:0:0:0:0 | Kotlin not-null Integer |
4-
| primitiveArrays.kt:5:56:5:76 | d | file://:0:0:0:0 | Integer[][] | file://:0:0:0:0 | Integer[] | file://:0:0:0:0 | Integer | file://:0:0:0:0 | Kotlin nullable Integer[] | file://:0:0:0:0 | Kotlin nullable Integer |
5-
| primitiveArrays.kt:5:79:5:98 | e | file://:0:0:0:0 | Integer[][] | file://:0:0:0:0 | Integer[] | file://:0:0:0:0 | Integer | file://:0:0:0:0 | Kotlin nullable Integer[] | file://:0:0:0:0 | Kotlin nullable Integer |
6-
| primitiveArrays.kt:5:101:5:118 | f | file://:0:0:0:0 | int[][] | file://:0:0:0:0 | int[] | file://:0:0:0:0 | int | file://:0:0:0:0 | Kotlin nullable int[] | file://:0:0:0:0 | Kotlin not-null Integer |
1+
| primitiveArrays.kt:5:12:5:24 | a | file://:0:0:0:0 | Integer[] | file://:0:0:0:0 | Integer | file://:0:0:0:0 | Integer |
2+
| primitiveArrays.kt:5:27:5:40 | b | file://:0:0:0:0 | Integer[] | file://:0:0:0:0 | Integer | file://:0:0:0:0 | Integer |
3+
| primitiveArrays.kt:5:43:5:53 | c | file://:0:0:0:0 | int[] | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
4+
| primitiveArrays.kt:5:56:5:76 | d | file://:0:0:0:0 | Integer[][] | file://:0:0:0:0 | Integer[] | file://:0:0:0:0 | Integer |
5+
| primitiveArrays.kt:5:79:5:98 | e | file://:0:0:0:0 | Integer[][] | file://:0:0:0:0 | Integer[] | file://:0:0:0:0 | Integer |
6+
| primitiveArrays.kt:5:101:5:118 | f | file://:0:0:0:0 | int[][] | file://:0:0:0:0 | int[] | file://:0:0:0:0 | int |

java/ql/test/kotlin/library-tests/arrays/test.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ import java
22

33
from Parameter p, Array a
44
where p.getType() = a and p.getFile().getBaseName() = "primitiveArrays.kt"
5-
select p, a, a.getComponentType(), a.getElementType(), a.getComponentKotlinType(), a.getElementKotlinType()
5+
select p, a, a.getComponentType(), a.getElementType()

0 commit comments

Comments
 (0)