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.
This commit is contained in:
Chris Smowton
2021-11-12 15:07:06 +00:00
committed by Ian Lynagh
parent 70294bd26b
commit 1b7e33b6e3
6 changed files with 70 additions and 60 deletions

View File

@@ -25,6 +25,7 @@ import java.util.zip.GZIPOutputStream
import com.semmle.extractor.java.OdasaOutput
import com.semmle.extractor.java.OdasaOutput.TrapFileManager
import com.semmle.util.files.FileUtil
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.*
import kotlin.system.exitProcess
@@ -372,6 +373,8 @@ open class KotlinUsesExtractor(
d.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
}
fun isArray(t: IrSimpleType) = t.isBoxedArray || t.isPrimitiveArray()
fun extractClassLaterIfExternal(c: IrClass) {
if (isExternalDeclaration(c)) {
extractExternalClassLater(c)
@@ -410,7 +413,7 @@ open class KotlinUsesExtractor(
else
primitiveInfo.primitiveName
type.isBoxedArray || type.isPrimitiveArray() -> {
isArray(type) -> {
val elementType = type.getArrayElementType(pluginContext.irBuiltIns)
val javaElementType = if (type.isPrimitiveArray()) elementType else elementType.makeNullable()
shortName(javaElementType) + "[]"
@@ -520,22 +523,52 @@ open class KotlinUsesExtractor(
return TypeResults(javaResult, kotlinResult)
}
fun useArrayType(arrayType: IrSimpleType, componentType: IrType, elementType: IrType, dimensions: Int): TypeResults {
// Given either a primitive array or a boxed array, returns primitive arrays unchanged,
// but returns boxed arrays with a nullable, invariant component type, with any nested arrays
// similarly transformed. For example, Array<out Array<in E>> would become Array<Array<E?>?>
// Array<*> will become Array<Any?>.
fun getInvariantNullableArrayType(arrayType: IrSimpleType): IrSimpleType =
if (arrayType.isPrimitiveArray())
arrayType
else {
val componentType = arrayType.getArrayElementType(pluginContext.irBuiltIns)
val componentTypeBroadened = when (componentType) {
is IrSimpleType ->
if (isArray(componentType)) getInvariantNullableArrayType(componentType) else componentType
else -> componentType
}
val unchanged =
componentType == componentTypeBroadened &&
(arrayType.arguments[0] as? IrTypeProjection)?.variance == Variance.INVARIANT &&
componentType.isNullable()
if (unchanged)
arrayType
else
IrSimpleTypeImpl(
arrayType.classifier,
true,
listOf(makeTypeProjection(componentTypeBroadened, Variance.INVARIANT)),
listOf()
)
}
fun useArrayType(arrayType: IrSimpleType, componentType: IrType, elementType: IrType, dimensions: Int, isPrimitiveArray: Boolean): TypeResults {
// Ensure we extract Array<Int> as Integer[], not int[], for example:
fun nullableIfNotPrimitive(type: IrType) = if (type.isPrimitiveType() && !isPrimitiveArray) type.makeNullable() else type
// TODO: Figure out what signatures should be returned
val componentTypeLabels = useType(componentType)
val elementTypeLabels = useType(elementType)
val componentTypeLabel = useType(nullableIfNotPrimitive(componentType)).javaResult.id
val elementTypeLabel = useType(nullableIfNotPrimitive(elementType)).javaResult.id
val id = tw.getLabelFor<DbArray>("@\"array;$dimensions;{${elementTypeLabels.javaResult.id}}\"") {
val id = tw.getLabelFor<DbArray>("@\"array;$dimensions;{${elementTypeLabel}}\"") {
tw.writeArrays(
it,
shortName(arrayType),
elementTypeLabels.javaResult.id,
elementTypeLabels.kotlinResult.id,
elementTypeLabel,
dimensions,
componentTypeLabels.javaResult.id,
componentTypeLabels.kotlinResult.id)
componentTypeLabel)
extractClassCommon(arrayType.classifier.owner as IrClass, it)
@@ -546,29 +579,23 @@ open class KotlinUsesExtractor(
// TODO: modifiers
// tw.writeHasModifier(length, getModifierKey("public"))
// tw.writeHasModifier(length, getModifierKey("final"))
}
val javaSignature = "an array" // TODO: Wrong
val javaResult = TypeResult(id, javaSignature)
// Note the stripping of any type projection from `componentType` here mirrors the action of `IrType.getArrayElementType`,
// 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>)
val owner: IrClass = arrayType.classifier.owner as IrClass
val kotlinTypeArgs = if (arrayType.arguments.isEmpty()) listOf() else listOf(makeTypeProjection(componentType, Variance.INVARIANT))
val kotlinClassName = getUnquotedClassLabel(owner, kotlinTypeArgs)
val kotlinSignature = "$javaSignature?" // TODO: Wrong
val kotlinLabel = "@\"kt_type;nullable;${kotlinClassName}\""
val kotlinId: Label<DbKt_nullable_type> = tw.getLabelFor(kotlinLabel, {
tw.writeKt_nullable_types(it, id)
})
val kotlinResult = TypeResult(kotlinId, kotlinSignature)
// Note we will only emit one `clone()` method per Java array type, so we choose `Array<C?>` as its Kotlin
// return type, where C is the component type with any nested arrays themselves invariant and nullable.
val kotlinCloneReturnType = getInvariantNullableArrayType(arrayType).makeNullable()
val kotlinCloneReturnTypeLabel = useType(kotlinCloneReturnType).kotlinResult.id
tw.getLabelFor<DbMethod>("@\"callable;{$id}.clone(){$id}\"") {
tw.writeMethods(it, "clone", "clone()", javaResult.id, kotlinResult.id, javaResult.id, it)
val clone = tw.getLabelFor<DbMethod>("@\"callable;{$it}.clone(){$it}\"")
tw.writeMethods(clone, "clone", "clone()", it, kotlinCloneReturnTypeLabel, it, clone)
// TODO: modifiers
// tw.writeHasModifier(clone, getModifierKey("public"))
}
return TypeResults(javaResult, kotlinResult)
val javaSignature = "an array" // TODO: Wrong
val javaResult = TypeResult(id, javaSignature)
val arrayClassResult = useSimpleTypeClass(arrayType.classifier.owner as IrClass, arrayType.arguments, arrayType.hasQuestionMark)
return TypeResults(javaResult, arrayClassResult.kotlinResult)
}
fun useSimpleType(s: IrSimpleType, canReturnPrimitiveTypes: Boolean): TypeResults {
@@ -669,13 +696,12 @@ class X {
elementType = elementType.getArrayElementType(pluginContext.irBuiltIns)
}
fun nullableUnlessPrimitive(type: IrType) = if (isPrimitiveArray && type.isPrimitiveType()) type else type.makeNullable()
return useArrayType(
s,
nullableUnlessPrimitive(componentType),
nullableUnlessPrimitive(elementType),
dimensions
componentType,
elementType,
dimensions,
isPrimitiveArray
)
}

View File

@@ -512,10 +512,8 @@ arrays(
unique int id: @array,
string nodeName: string ref,
int elementtypeid: @type ref,
int elementkttypeid: @kt_type ref,
int dimension: int ref,
int componenttypeid: @type ref,
int componentkttypeid: @kt_type ref
int componenttypeid: @type ref
);
enclInReftype(

View File

@@ -40,7 +40,7 @@ predicate hasName(Element e, string name) {
or
wildcards(e, name, _)
or
arrays(e, name, _, _, _, _, _)
arrays(e, name, _, _, _)
or
modifiers(e, name)
or

View File

@@ -354,35 +354,21 @@ class Array extends RefType, @array {
*
* For example, the component type of `Object[][]` is `Object[]`.
*/
Type getComponentType() { arrays(this, _, _, _, _, result, _) }
Type getComponentType() { arrays(this, _, _, _, result) }
/**
* Gets the type of the components of this array type.
*
* For example, the component type of `Object[][]` is `Object[]`.
*/
KotlinType getComponentKotlinType() { arrays(this, _, _, _, _, _, result) }
/**
* Gets the Kotlin type of the elements used to construct this array type.
* Gets the type of the elements used to construct this array type.
*
* For example, the element type of `Object[][]` is `Object`.
*/
Type getElementType() { arrays(this, _, result, _, _, _, _) }
/**
* Gets the Kotlin type of the elements used to construct this array type.
*
* For example, the element type of `Object[][]` is `Object`.
*/
KotlinType getElementKotlinType() { arrays(this, _, _, result, _, _, _) }
Type getElementType() { arrays(this, _, result, _, _) }
/**
* Gets the arity of this array type.
*
* For example, the dimension of `Object[][]` is 2.
*/
int getDimension() { arrays(this, _, _, _, result, _, _) }
int getDimension() { arrays(this, _, _, result, _) }
/**
* Gets the JVM descriptor for this type, as used in bytecode.

View File

@@ -1,6 +1,6 @@
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |

View File

@@ -2,4 +2,4 @@ import java
from Parameter p, Array a
where p.getType() = a and p.getFile().getBaseName() = "primitiveArrays.kt"
select p, a, a.getComponentType(), a.getElementType(), a.getComponentKotlinType(), a.getElementKotlinType()
select p, a, a.getComponentType(), a.getElementType()