Extract anonymous object creation

This commit is contained in:
Tamas Vajk
2021-11-16 12:58:36 +01:00
committed by Ian Lynagh
parent ab6b500475
commit b7e0828f78
12 changed files with 230 additions and 18 deletions

View File

@@ -372,6 +372,10 @@ open class KotlinUsesExtractor(
}
fun useClassInstance(c: IrClass, typeArgs: List<IrTypeArgument>): UseClassInstanceResult {
if (c.isAnonymousObject) {
logger.warn(Severity.ErrorSevere, "Unexpected access to anonymous class instance")
}
// TODO: only substitute in class and function signatures
// because within function bodies we can get things like Unit.INSTANCE
// and List.asIterable (an extension, i.e. static, method)
@@ -432,7 +436,22 @@ open class KotlinUsesExtractor(
classLabelResult.shortName)
}
open fun useAnonymousClass(c: IrClass): TypeResults {
throw Exception("Anonymous classes can only be accessed through source file extraction")
}
fun useSimpleTypeClass(c: IrClass, args: List<IrTypeArgument>, hasQuestionMark: Boolean): TypeResults {
if (c.isAnonymousObject) {
if (args.isNotEmpty()) {
logger.warn(Severity.ErrorHigh, "Anonymous class with unexpected type arguments")
}
if (hasQuestionMark) {
logger.warn(Severity.ErrorHigh, "Unexpected nullable anonymous class")
}
return useAnonymousClass(c)
}
val classInstanceResult = useClassInstance(c, args)
val javaClassId = classInstanceResult.typeResult.id
val kotlinQualClassName = getUnquotedClassLabel(c, args).classLabel
@@ -785,7 +804,12 @@ class X {
)
}
fun getClassLabel(c: IrClass, typeArgs: List<IrTypeArgument>): ClassLabelResults {
if (c.isAnonymousObject) {
logger.warn(Severity.ErrorSevere, "Label generation should not be requested for an anonymous class")
}
val unquotedLabel = getUnquotedClassLabel(c, typeArgs)
return ClassLabelResults(
"@\"class;${unquotedLabel.classLabel}\"",
@@ -793,6 +817,11 @@ class X {
}
fun useClassSource(c: IrClass): Label<out DbClassorinterface> {
if (c.isAnonymousObject) {
@Suppress("UNCHECKED_CAST")
return useAnonymousClass(c).javaResult.id as Label<DbClass>
}
// For source classes, the label doesn't include and type arguments
val classId = getClassLabel(c, listOf())
return tw.getLabelFor(classId.classLabel)
@@ -1052,8 +1081,28 @@ open class KotlinFileExtractor(
return id
}
private val anonymousTypeMap: MutableMap<IrClass, TypeResults> = mutableMapOf()
override fun useAnonymousClass(c: IrClass): TypeResults {
var res = anonymousTypeMap[c]
if (res == null) {
val javaResult = TypeResult(tw.getFreshIdLabel<DbClass>(), "", "")
val kotlinResult = TypeResult(tw.getFreshIdLabel<DbKt_notnull_type>() , "", "")
tw.writeKt_notnull_types(kotlinResult.id, javaResult.id)
res = TypeResults(javaResult, kotlinResult)
anonymousTypeMap[c] = res
}
return res
}
fun extractClassSource(c: IrClass): Label<out DbClassorinterface> {
val id = useClassSource(c)
val id = if (c.isAnonymousObject) {
@Suppress("UNCHECKED_CAST")
useAnonymousClass(c).javaResult.id as Label<out DbClass>
} else {
useClassSource(c)
}
val pkg = c.packageFqName?.asString() ?: ""
val cls = c.name.asString()
val pkgId = extractPackage(pkg)
@@ -1074,24 +1123,36 @@ open class KotlinFileExtractor(
val locId = tw.getLocation(c)
tw.writeHasLocation(id, locId)
val parent = c.parent
if (parent is IrClass) {
val parentId = useClassInstance(parent, listOf()).typeResult.id
tw.writeEnclInReftype(id, parentId)
if(c.isCompanion) {
// If we are a companion then our parent has a
// public static final ParentClass$CompanionObjectClass CompanionObjectName;
// that we need to fabricate here
val instance = useCompanionObjectClassInstance(c)
if(instance != null) {
val type = useSimpleTypeClass(c, emptyList(), false)
tw.writeFields(instance.id, instance.name, type.javaResult.id, type.kotlinResult.id, id, instance.id)
tw.writeHasLocation(instance.id, locId)
addModifiers(instance.id, "public", "static", "final")
@Suppress("UNCHECKED_CAST")
tw.writeClass_companion_object(parentId as Label<DbClass>, instance.id, id as Label<DbClass>)
var parent: IrDeclarationParent? = c.parent
while (parent != null) {
if (parent is IrClass) {
val parentId =
if (parent.isAnonymousObject) {
@Suppress("UNCHECKED_CAST")
useAnonymousClass(c).javaResult.id as Label<out DbClass>
} else {
useClassInstance(parent, listOf()).typeResult.id
}
tw.writeEnclInReftype(id, parentId)
if(c.isCompanion) {
// If we are a companion then our parent has a
// public static final ParentClass$CompanionObjectClass CompanionObjectName;
// that we need to fabricate here
val instance = useCompanionObjectClassInstance(c)
if(instance != null) {
val type = useSimpleTypeClass(c, emptyList(), false)
tw.writeFields(instance.id, instance.name, type.javaResult.id, type.kotlinResult.id, id, instance.id)
tw.writeHasLocation(instance.id, locId)
addModifiers(instance.id, "public", "static", "final")
@Suppress("UNCHECKED_CAST")
tw.writeClass_companion_object(parentId as Label<DbClass>, instance.id, id as Label<DbClass>)
}
}
break
}
parent = (parent as? IrDeclaration)?.parent
}
c.typeParameters.map { extractTypeParameter(it) }
@@ -1390,6 +1451,13 @@ open class KotlinFileExtractor(
is IrVariable -> {
extractVariable(s, callable, parent, idx)
}
is IrClass -> {
if (s.isAnonymousObject) {
logger.info("Skipping extracting anonymous object class. It will be extracted later where it's instantiated.")
} else {
logger.warnElement(Severity.ErrorSevere, "Found non anonymous IrClass as IrStatement: " + s.javaClass, s)
}
}
else -> {
logger.warnElement(Severity.ErrorSevere, "Unrecognised IrStatement: " + s.javaClass, s)
}
@@ -1706,7 +1774,22 @@ open class KotlinFileExtractor(
callable: Label<out DbCallable>
) {
val id = tw.getFreshIdLabel<DbNewexpr>()
val type = useType(e.type)
val type: TypeResults
val isAnonymous = ((e.type as? IrSimpleType)?.classifier?.owner as? IrClass)?.isAnonymousObject ?: false
if (isAnonymous) {
if (e.typeArgumentsCount > 0) {
logger.warn("Unexpected type arguments for anonymous class constructor call")
}
val c = (e.type as IrSimpleType).classifier.owner as IrClass
@Suppress("UNCHECKED_CAST")
val classId = extractClassSource(c) as Label<out DbClass>
tw.writeIsAnonymClass(classId, id)
type = useAnonymousClass(c)
} else {
type = useType(e.type)
}
val locId = tw.getLocation(e)
val methodId = useFunction<DbConstructor>(e.symbol.owner)
tw.writeExprs_newexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx)

View File

@@ -0,0 +1,7 @@
| classes.kt:66:20:66:54 | (no string representation) | classes.kt:66:20:66:54 | new <no name provided>(...) |
| classes.kt:68:20:68:74 | (no string representation) | classes.kt:68:20:68:74 | new <no name provided>(...) |
| classes.kt:72:16:77:10 | (no string representation) | classes.kt:72:16:77:10 | new <no name provided>(...) |
| classes.kt:75:24:75:33 | (no string representation) | classes.kt:75:24:75:33 | new <no name provided>(...) |
| classes.kt:81:16:81:38 | (no string representation) | classes.kt:81:16:81:38 | new <no name provided>(...) |
| classes.kt:85:16:85:25 | (no string representation) | classes.kt:85:16:85:25 | new <no name provided>(...) |
| classes.kt:89:16:89:44 | (no string representation) | classes.kt:89:16:89:44 | new <no name provided>(...) |

View File

@@ -0,0 +1,6 @@
import java
from AnonymousClass c
where c.fromSource()
select c, c.getClassInstanceExpr()

View File

@@ -8,3 +8,11 @@
| classes.kt:34:1:47:1 | ClassSeven | ClassSeven |
| classes.kt:49:1:51:1 | Direction | Direction |
| classes.kt:53:1:57:1 | Color | Color |
| classes.kt:63:1:91:1 | Class1 | Class1 |
| classes.kt:66:20:66:54 | (no string representation) | <anonymous class> |
| classes.kt:68:20:68:74 | (no string representation) | <anonymous class> |
| classes.kt:72:16:77:10 | (no string representation) | <anonymous class> |
| classes.kt:75:24:75:33 | (no string representation) | <anonymous class> |
| classes.kt:81:16:81:38 | (no string representation) | <anonymous class> |
| classes.kt:85:16:85:25 | (no string representation) | <anonymous class> |
| classes.kt:89:16:89:44 | (no string representation) | <anonymous class> |

View File

@@ -55,3 +55,37 @@ enum class Color(val rgb: Int) {
GREEN(0x00FF00),
BLUE(0x0000FF)
}
interface Interface1 {}
interface Interface2 {}
interface Interface3<T> {}
class Class1 {
private fun getObject1(b: Boolean) : Any {
if (b)
return object : Interface1, Interface2 { }
else
return object : Interface1, Interface2, Interface3<String> { }
}
private fun getObject2() : Interface1 {
return object : Interface1, Interface2 {
val x = 1
fun foo(): Any {
return object { }
}
}
}
private fun getObject3() : Any {
return object : Interface1 { }
}
private fun getObject4() : Any {
return object { }
}
private fun getObject5() : Any {
return object : Interface3<Int?> { }
}
}

View File

@@ -8,3 +8,11 @@ superCall
| classes.kt:17:18:17:28 | super(...) |
| classes.kt:28:19:28:29 | super(...) |
| classes.kt:35:27:35:27 | super(...) |
| classes.kt:63:1:91:1 | super(...) |
| classes.kt:66:20:66:54 | super(...) |
| classes.kt:68:20:68:74 | super(...) |
| classes.kt:72:16:77:10 | super(...) |
| classes.kt:75:24:75:33 | super(...) |
| classes.kt:81:16:81:38 | super(...) |
| classes.kt:85:16:85:25 | super(...) |
| classes.kt:89:16:89:44 | super(...) |

View File

@@ -10,6 +10,17 @@ initBlocks
| classes.kt:34:1:47:1 | <obinit> |
| classes.kt:49:1:51:1 | <obinit> |
| classes.kt:53:1:57:1 | <obinit> |
| classes.kt:59:1:59:23 | <obinit> |
| classes.kt:60:1:60:23 | <obinit> |
| classes.kt:61:1:61:26 | <obinit> |
| classes.kt:63:1:91:1 | <obinit> |
| classes.kt:66:20:66:54 | <obinit> |
| classes.kt:68:20:68:74 | <obinit> |
| classes.kt:72:16:77:10 | <obinit> |
| classes.kt:75:24:75:33 | <obinit> |
| classes.kt:81:16:81:38 | <obinit> |
| classes.kt:85:16:85:25 | <obinit> |
| classes.kt:89:16:89:44 | <obinit> |
initCall
| classes.kt:2:1:2:18 | <obinit>(...) |
| classes.kt:4:1:6:1 | <obinit>(...) |
@@ -20,6 +31,14 @@ initCall
| classes.kt:35:5:37:5 | <obinit>(...) |
| classes.kt:49:1:51:1 | <obinit>(...) |
| classes.kt:53:1:57:1 | <obinit>(...) |
| classes.kt:63:1:91:1 | <obinit>(...) |
| classes.kt:66:20:66:54 | <obinit>(...) |
| classes.kt:68:20:68:74 | <obinit>(...) |
| classes.kt:72:16:77:10 | <obinit>(...) |
| classes.kt:75:24:75:33 | <obinit>(...) |
| classes.kt:81:16:81:38 | <obinit>(...) |
| classes.kt:85:16:85:25 | <obinit>(...) |
| classes.kt:89:16:89:44 | <obinit>(...) |
initExpressions
| classes.kt:4:17:4:28 | ...=... | 0 |
| classes.kt:5:5:5:18 | ...=... | 1 |
@@ -27,3 +46,4 @@ initExpressions
| classes.kt:42:5:42:18 | ...=... | 1 |
| classes.kt:45:9:45:18 | f(...) | 2 |
| classes.kt:53:18:53:29 | ...=... | 0 |
| classes.kt:73:13:73:21 | ...=... | 0 |

View File

@@ -1,2 +1,5 @@
| classes.kt:20:1:22:1 | IF1 |
| classes.kt:24:1:26:1 | IF2 |
| classes.kt:59:1:59:23 | Interface1 |
| classes.kt:60:1:60:23 | Interface2 |
| classes.kt:61:1:61:26 | Interface3 |

View File

@@ -9,3 +9,15 @@
| classes.kt:34:1:47:1 | ClassSeven | Object | Object.class | 0 | 0 | 0 | 0 |
| classes.kt:49:1:51:1 | Direction | Enum<Direction> | Enum.class | 0 | 0 | 0 | 0 |
| classes.kt:53:1:57:1 | Color | Enum<Color> | Enum.class | 0 | 0 | 0 | 0 |
| classes.kt:63:1:91:1 | Class1 | Object | Object.class | 0 | 0 | 0 | 0 |
| classes.kt:66:20:66:54 | (no string representation) | Interface1 | classes.kt | 59 | 1 | 59 | 23 |
| classes.kt:66:20:66:54 | (no string representation) | Interface2 | classes.kt | 60 | 1 | 60 | 23 |
| classes.kt:68:20:68:74 | (no string representation) | Interface1 | classes.kt | 59 | 1 | 59 | 23 |
| classes.kt:68:20:68:74 | (no string representation) | Interface2 | classes.kt | 60 | 1 | 60 | 23 |
| classes.kt:68:20:68:74 | (no string representation) | Interface3<String> | classes.kt | 61 | 1 | 61 | 26 |
| classes.kt:72:16:77:10 | (no string representation) | Interface1 | classes.kt | 59 | 1 | 59 | 23 |
| classes.kt:72:16:77:10 | (no string representation) | Interface2 | classes.kt | 60 | 1 | 60 | 23 |
| classes.kt:75:24:75:33 | (no string representation) | Object | Object.class | 0 | 0 | 0 | 0 |
| classes.kt:81:16:81:38 | (no string representation) | Interface1 | classes.kt | 59 | 1 | 59 | 23 |
| classes.kt:85:16:85:25 | (no string representation) | Object | Object.class | 0 | 0 | 0 | 0 |
| classes.kt:89:16:89:44 | (no string representation) | Interface3<Integer> | classes.kt | 61 | 1 | 61 | 26 |

View File

@@ -16,3 +16,4 @@
| exprs.kt:54:12:54:23 | ... > ... | exprs.kt:54:12:54:19 | variable | exprs.kt:54:23:54:23 | 0 |
| exprs.kt:58:12:58:20 | ... + ... | exprs.kt:58:12:58:14 | 123 | exprs.kt:58:18:58:20 | 456 |
| exprs.kt:84:8:84:16 | ... != ... | exprs.kt:84:8:84:8 | r | exprs.kt:84:13:84:16 | null |
| exprs.kt:113:31:113:37 | ... + ... | exprs.kt:113:31:113:32 | <get-a1>(...) | exprs.kt:113:36:113:37 | a2 |

View File

@@ -223,6 +223,24 @@
| exprs.kt:102:27:102:31 | SOUTH | exprs.kt:101:1:104:1 | enums | VarAccess |
| exprs.kt:103:5:103:27 | green | exprs.kt:101:1:104:1 | enums | LocalVariableDeclExpr |
| exprs.kt:103:23:103:27 | GREEN | exprs.kt:101:1:104:1 | enums | VarAccess |
| exprs.kt:108:1:116:1 | <obinit>(...) | exprs.kt:108:1:116:1 | Class1 | MethodAccess |
| exprs.kt:109:5:109:14 | ...=... | exprs.kt:108:1:116:1 | <obinit> | AssignExpr |
| exprs.kt:109:5:109:14 | a1 | exprs.kt:108:1:116:1 | <obinit> | VarAccess |
| exprs.kt:109:5:109:14 | a1 | exprs.kt:109:5:109:14 | <get-a1> | VarAccess |
| exprs.kt:109:14:109:14 | 1 | exprs.kt:108:1:116:1 | <obinit> | IntegerLiteral |
| exprs.kt:111:9:111:18 | a2 | exprs.kt:110:13:115:5 | getObject | LocalVariableDeclExpr |
| exprs.kt:111:18:111:18 | 2 | exprs.kt:110:13:115:5 | getObject | IntegerLiteral |
| exprs.kt:112:16:114:9 | <Stmt> | exprs.kt:110:13:115:5 | getObject | StmtExpr |
| exprs.kt:112:16:114:9 | <obinit>(...) | exprs.kt:112:16:114:9 | <no name provided> | MethodAccess |
| exprs.kt:112:16:114:9 | new <no name provided>(...) | exprs.kt:110:13:115:5 | getObject | ClassInstanceExpr |
| exprs.kt:113:13:113:49 | ...=... | exprs.kt:112:16:114:9 | <obinit> | AssignExpr |
| exprs.kt:113:13:113:49 | a3 | exprs.kt:112:16:114:9 | <obinit> | VarAccess |
| exprs.kt:113:13:113:49 | a3 | exprs.kt:113:13:113:49 | <get-a3> | VarAccess |
| exprs.kt:113:31:113:32 | <get-a1>(...) | exprs.kt:112:16:114:9 | <obinit> | MethodAccess |
| exprs.kt:113:31:113:32 | this | exprs.kt:112:16:114:9 | <obinit> | ThisAccess |
| exprs.kt:113:31:113:37 | ... + ... | exprs.kt:112:16:114:9 | <obinit> | AddExpr |
| exprs.kt:113:36:113:37 | a2 | exprs.kt:112:16:114:9 | <obinit> | VarAccess |
| exprs.kt:113:40:113:49 | toString(...) | exprs.kt:112:16:114:9 | <obinit> | MethodAccess |
| file://:0:0:0:0 | Color | exprs.kt:95:6:99:1 | Color | TypeAccess |
| file://:0:0:0:0 | Direction | exprs.kt:91:6:93:1 | Direction | TypeAccess |
| file://:0:0:0:0 | height | exprs.kt:82:1:89:1 | foo | VarAccess |

View File

@@ -102,3 +102,15 @@ fun enums() {
val south = Direction.SOUTH
val green = Color.GREEN
}
interface Interface1 {}
class Class1 {
val a1 = 1
private fun getObject() : Any {
val a2 = 2
return object : Interface1 {
val a3: String = (a1 + a2).toString()
}
}
}