Skip to content

Commit

Permalink
fix: initial types that can hold any value should be top
Browse files Browse the repository at this point in the history
  • Loading branch information
SirYwell committed Jun 25, 2024
1 parent 622f8ee commit e0a5929
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 15 deletions.
56 changes: 44 additions & 12 deletions src/main/kotlin/de/sirywell/handlehints/dfa/SsaAnalyzer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
if (isUnrelated(instruction.variable)) return
val element = controlFlow.getElement(index)
if (element is PsiReferenceExpression && isUnstableVariable(element, instruction.variable)) {
typeData[element] = bottomForType(instruction.variable.type, instruction.variable)
typeData[element] = topForType(instruction.variable.type, instruction.variable)
return
}
val value = ssaConstruction.readVariable(instruction.variable, block)
Expand Down Expand Up @@ -217,9 +217,9 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
"parameterList",
"parameterType",
"toMethodDescriptorString",
-> noMethodHandle()
-> unrelatedType()

in objectMethods -> noMethodHandle()
in objectMethods -> unrelatedType()
else -> warnUnsupported(expression, "MethodType")
}
} else if (receiverIsMethodHandles(expression)) {
Expand All @@ -245,21 +245,27 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
"describeConstable",
"invoke",
"invokeExact",
"invokeWithArguments" -> noMethodHandle()
"invokeWithArguments" -> unrelatedType()

"type" -> qualifier?.methodHandleType(block)
?.withVarargs(TriState.NO) // if ever used somewhere else, assume non-varargs

in objectMethods -> noMethodHandle()
in objectMethods -> unrelatedType()
else -> warnUnsupported(expression, "MethodHandle")
}
} else if (receiverIsLookup(expression)) {
return lookup(expression, arguments, block)
} else {
return methodExpression.type?.let { topForType(it, expression) }
}
} else if (expression is PsiReferenceExpression) {
val variable = expression.resolve() as? PsiVariable ?: return noMatch()
if (isUnstableVariable(expression, variable)) {
return bottomForType(variable.type, variable)
return topForType(variable.type, variable)
}
// parameters are always top - could be anything
if (variable is PsiParameter) {
return topForType(variable.type, variable)
}
val value = ssaConstruction.readVariable(variable, block)
return if (value is Holder) {
Expand Down Expand Up @@ -337,9 +343,9 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
"unreflectSetter",
"unreflectSpecial",
"unreflectVarHandle"
-> noMethodHandle()
-> unrelatedType()

in objectMethods -> noMethodHandle()
in objectMethods -> unrelatedType()
else -> warnUnsupported(expression, "MethodHandles.Lookup")
}
}
Expand All @@ -353,7 +359,7 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
return resolver(refc, type)
}

private fun noMethodHandle(): MethodHandleType? = null
private fun unrelatedType(): TypeLatticeElement<*>? = null

private fun methodHandles(
expression: PsiMethodCallExpression,
Expand Down Expand Up @@ -551,9 +557,9 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
"lookup",
"privateLookupIn",
"publicLookup",
"reflectAs" -> noMethodHandle()
"reflectAs" -> unrelatedType()

in objectMethods -> noMethodHandle()
in objectMethods -> unrelatedType()
else -> warnUnsupported(expression, "MethodHandles")
}
}
Expand All @@ -563,7 +569,7 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
}

private inline fun <reified T : TypeLatticeElement<T>> notConstant(): T {
return bottomForType<T>()
return topForType<T>()
}

private fun singleParameter(
Expand Down Expand Up @@ -642,4 +648,30 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
}
}

private inline fun <reified T : TypeLatticeElement<*>> topForType(): T {
return topForType(T::class)
}

private fun <T : TypeLatticeElement<*>> topForType(clazz: KClass<T>): T {
if (clazz == MethodHandleType::class) {
return clazz.java.cast(TopMethodHandleType)
}
if (clazz == VarHandleType::class) {
return clazz.java.cast(TopVarHandleType)
}
if (clazz == Type::class) {
return clazz.java.cast(TopType)
}
throw UnsupportedOperationException("$clazz is not supported")
}

private fun topForType(psiType: PsiType, context: PsiElement): TypeLatticeElement<*> {
return when (psiType) {
methodTypeType(context) -> topForType<MethodHandleType>()
methodHandleType(context) -> topForType<MethodHandleType>()
varHandleType(context) -> topForType<VarHandleType>()
else -> throw UnsupportedOperationException("${psiType.presentableText} is not supported")
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class MethodHandleInspectionsTest : LightJavaCodeInsightFixtureTestCase() {

fun testFinalFields() = doTypeCheckingTest()

fun testInitialTypes() = doTypeCheckingTest()

fun testLookupFindConstructor() = doTypeCheckingTest()

fun testLookupFindGetter() = doTypeCheckingTest()
Expand Down
6 changes: 3 additions & 3 deletions src/test/testData/FinalFields.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ public class FinalFields {
<info descr="(CharSequence)int">this.methodType = <info descr="(CharSequence)int">MethodType.methodType(int.class, CharSequence.class)</info></info>;
<info descr="(CharSequence)int">MethodHandle mn = <info descr="(CharSequence)int">MethodHandles.empty(this.methodType)</info>;</info>
<info descr="(CharSequence)int">MethodHandle mni = <info descr="(CharSequence)int">MethodHandles.empty(methodType)</info>;</info>
<info descr="BotMethodHandleType">MethodHandle o = <info descr="BotMethodHandleType">MethodHandles.empty(other.methodType)</info>;</info>
<info descr="TopMethodHandleType">MethodHandle o = <info descr="TopMethodHandleType">MethodHandles.empty(other.methodType)</info>;</info>
if (Math.random() < 0.5) {
other = this;
<info descr="BotMethodHandleType">MethodHandle oy = <info descr="BotMethodHandleType">MethodHandles.empty(other.methodType)</info>;</info>
<info descr="TopMethodHandleType">MethodHandle oy = <info descr="TopMethodHandleType">MethodHandles.empty(other.methodType)</info>;</info>
}
<info descr="BotMethodHandleType">MethodHandle om = <info descr="BotMethodHandleType">MethodHandles.empty(other.methodType)</info>;</info>
<info descr="TopMethodHandleType">MethodHandle om = <info descr="TopMethodHandleType">MethodHandles.empty(other.methodType)</info>;</info>
}

void m() {
Expand Down
29 changes: 29 additions & 0 deletions src/test/testData/InitialTypes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class InitialTypes {

void m(MethodType mt, boolean b, Inner inner) {
MethodHandle mh0;
if (b) {
<info descr="TopMethodHandleType">mh0 = <info descr="TopMethodHandleType">MethodHandles.empty(mt)</info></info>;
} else {
<info descr="()int">mh0 = <info descr="()int">MethodHandles.zero(int.class)</info></info>;
}
<info descr="TopMethodHandleType">MethodHandle mh1 = <info descr="TopMethodHandleType">myMethodHandleMethod()</info>;</info>
<info descr="TopMethodHandleType">MethodHandle mh2 = inner.methodHandle;</info>
<info descr="TopMethodHandleType">MethodHandle mh3 = create().methodHandle;</info>
}

private MethodHandle myMethodHandleMethod() {
return MethodHandles.zero(Object.class);
}

private Inner create() {
return new Inner();
}
static class Inner {
MethodHandle methodHandle;
}
}

0 comments on commit e0a5929

Please sign in to comment.