Skip to content

Commit ddb4cfd

Browse files
committed
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 d9cf1a5 commit ddb4cfd

File tree

6 files changed

+70
-60
lines changed

6 files changed

+70
-60
lines changed

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 org.jetbrains.kotlin.types.Variance
@@ -373,6 +374,8 @@ open class KotlinUsesExtractor(
373374
d.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
374375
}
375376

377+
fun isArray(t: IrSimpleType) = t.isBoxedArray || t.isPrimitiveArray()
378+
376379
fun extractClassLaterIfExternal(c: IrClass) {
377380
if (isExternalDeclaration(c)) {
378381
extractExternalClassLater(c)
@@ -411,7 +414,7 @@ open class KotlinUsesExtractor(
411414
else
412415
primitiveInfo.primitiveName
413416

414-
type.isBoxedArray || type.isPrimitiveArray() -> {
417+
isArray(type) -> {
415418
val elementType = type.getArrayElementType(pluginContext.irBuiltIns)
416419
val javaElementType = if (type.isPrimitiveArray()) elementType else elementType.makeNullable()
417420
shortName(javaElementType) + "[]"
@@ -521,22 +524,52 @@ open class KotlinUsesExtractor(
521524
return TypeResults(javaResult, kotlinResult)
522525
}
523526

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
525560

526561
// TODO: Figure out what signatures should be returned
527562

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
530565

531-
val id = tw.getLabelFor<DbArray>("@\"array;$dimensions;{${elementTypeLabels.javaResult.id}}\"") {
566+
val id = tw.getLabelFor<DbArray>("@\"array;$dimensions;{${elementTypeLabel}}\"") {
532567
tw.writeArrays(
533568
it,
534569
shortName(arrayType),
535-
elementTypeLabels.javaResult.id,
536-
elementTypeLabels.kotlinResult.id,
570+
elementTypeLabel,
537571
dimensions,
538-
componentTypeLabels.javaResult.id,
539-
componentTypeLabels.kotlinResult.id)
572+
componentTypeLabel)
540573

541574
extractClassCommon(arrayType.classifier.owner as IrClass, it)
542575

@@ -547,29 +580,23 @@ open class KotlinUsesExtractor(
547580
// TODO: modifiers
548581
// tw.writeHasModifier(length, getModifierKey("public"))
549582
// tw.writeHasModifier(length, getModifierKey("final"))
550-
}
551583

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
565588

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)
568591
// TODO: modifiers
569592
// tw.writeHasModifier(clone, getModifierKey("public"))
570593
}
571594

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)
573600
}
574601

575602
fun useSimpleType(s: IrSimpleType, canReturnPrimitiveTypes: Boolean): TypeResults {
@@ -670,13 +697,12 @@ class X {
670697
elementType = elementType.getArrayElementType(pluginContext.irBuiltIns)
671698
}
672699

673-
fun nullableUnlessPrimitive(type: IrType) = if (isPrimitiveArray && type.isPrimitiveType()) type else type.makeNullable()
674-
675700
return useArrayType(
676701
s,
677-
nullableUnlessPrimitive(componentType),
678-
nullableUnlessPrimitive(elementType),
679-
dimensions
702+
componentType,
703+
elementType,
704+
dimensions,
705+
isPrimitiveArray
680706
)
681707
}
682708

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
@@ -298,35 +298,21 @@ class Array extends RefType, @array {
298298
*
299299
* For example, the component type of `Object[][]` is `Object[]`.
300300
*/
301-
Type getComponentType() { arrays(this, _, _, _, _, result, _) }
301+
Type getComponentType() { arrays(this, _, _, _, result) }
302302

303303
/**
304-
* Gets the type of the components of this array type.
305-
*
306-
* For example, the component type of `Object[][]` is `Object[]`.
307-
*/
308-
KotlinType getComponentKotlinType() { arrays(this, _, _, _, _, _, result) }
309-
310-
/**
311-
* Gets the Kotlin type of the elements used to construct this array type.
312-
*
313-
* For example, the element type of `Object[][]` is `Object`.
314-
*/
315-
Type getElementType() { arrays(this, _, result, _, _, _, _) }
316-
317-
/**
318-
* Gets the Kotlin type of the elements used to construct this array type.
304+
* Gets the type of the elements used to construct this array type.
319305
*
320306
* For example, the element type of `Object[][]` is `Object`.
321307
*/
322-
KotlinType getElementKotlinType() { arrays(this, _, _, result, _, _, _) }
308+
Type getElementType() { arrays(this, _, result, _, _) }
323309

324310
/**
325311
* Gets the arity of this array type.
326312
*
327313
* For example, the dimension of `Object[][]` is 2.
328314
*/
329-
int getDimension() { arrays(this, _, _, _, result, _, _) }
315+
int getDimension() { arrays(this, _, _, result, _) }
330316

331317
/**
332318
* 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)