Skip to content

Commit fe917e0

Browse files
authored
Merge pull request github#100 from github/smowton/admin/remove-array-kt-types
Remove kotlin element and component types from arrays table
2 parents eab34bc + 5e379a7 commit fe917e0

File tree

8 files changed

+797
-76
lines changed

8 files changed

+797
-76
lines changed

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

Lines changed: 93 additions & 54 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,11 @@ open class KotlinUsesExtractor(
411414
else
412415
primitiveInfo.primitiveName
413416

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+
}
415422

416423
type.classifier.owner is IrClass -> {
417424
val c = type.classifier.owner as IrClass
@@ -517,6 +524,81 @@ open class KotlinUsesExtractor(
517524
return TypeResults(javaResult, kotlinResult)
518525
}
519526

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+
520602
fun useSimpleType(s: IrSimpleType, canReturnPrimitiveTypes: Boolean): TypeResults {
521603
if (s.abbreviation != null) {
522604
// TODO: Extract this information
@@ -604,67 +686,24 @@ class X {
604686
*/
605687

606688
(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-
611689
var dimensions = 1
690+
var isPrimitiveArray = s.isPrimitiveArray()
612691
val componentType = s.getArrayElementType(pluginContext.irBuiltIns)
613692
var elementType = componentType
614693
while (elementType.isBoxedArray || elementType.isPrimitiveArray()) {
615694
dimensions++
695+
if(elementType.isPrimitiveArray())
696+
isPrimitiveArray = true
616697
elementType = elementType.getArrayElementType(pluginContext.irBuiltIns)
617698
}
618699

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+
)
668707
}
669708

670709
s.classifier.owner is IrClass -> {

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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package test
2+
3+
class Test {
4+
5+
fun test(a: Array<Int>, b: Array<Int?>, c: IntArray, d: Array<Array<Int?>>, e: Array<Array<Int>>, f: Array<IntArray>) { }
6+
7+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
cloneMethods
2+
| file://:0:0:0:0 | clone | clone() | file://:0:0:0:0 | Integer[] | file://:0:0:0:0 | Integer[] | file://:0:0:0:0 | Kotlin nullable Array<Integer> |
3+
| file://:0:0:0:0 | clone | clone() | file://:0:0:0:0 | Integer[][] | file://:0:0:0:0 | Integer[][] | file://:0:0:0:0 | Kotlin nullable Array<Integer[]> |
4+
| file://:0:0:0:0 | clone | clone() | file://:0:0:0:0 | int[] | file://:0:0:0:0 | int[] | file://:0:0:0:0 | Kotlin nullable IntArray |
5+
| file://:0:0:0:0 | clone | clone() | file://:0:0:0:0 | int[][] | file://:0:0:0:0 | int[][] | file://:0:0:0:0 | Kotlin nullable Array<int[]> |
6+
#select
7+
| 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 not-null Array<Integer> |
8+
| 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 not-null Array<Integer> |
9+
| 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 IntArray |
10+
| 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 not-null Array<Integer[]> |
11+
| 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 not-null Array<Integer[]> |
12+
| 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 not-null Array<int[]> |
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import java
2+
3+
class InterestingParameter extends Parameter {
4+
InterestingParameter() {
5+
this.getFile().getBaseName() = "primitiveArrays.kt"
6+
}
7+
}
8+
9+
from InterestingParameter p, Array a, KotlinType ktType
10+
where p.getType() = a and ktType = p.getKotlinType()
11+
select p, a, a.getComponentType(), a.getElementType(), ktType
12+
13+
query predicate cloneMethods(Method m, string signature, Array declType, Type returnType, KotlinType ktReturnType) {
14+
any(InterestingParameter p).getType() = declType and
15+
signature = m.getSignature() and
16+
declType = m.getDeclaringType() and
17+
returnType = m.getReturnType() and
18+
ktReturnType = m.getReturnKotlinType()
19+
}

0 commit comments

Comments
 (0)