Skip to content

Commit

Permalink
refactor: flatten MethodHandleType type structure (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
SirYwell committed Jun 24, 2024
1 parent bf2d5c9 commit 2222c27
Show file tree
Hide file tree
Showing 13 changed files with 324 additions and 330 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ class MethodTypeInlayHintsCollector : SharedBypassCollector {
val typeData = TypeData.forFile(element.containingFile)
val type = typeData[element] ?: return
// don't print
if (type is MethodHandleType && (type.signature is TopSignature || type.signature is BotSignature)) {
return
}
if (type is TopVarHandleType || type is BotVarHandleType) {
if (type is TopTypeLatticeElement || type is BotTypeLatticeElement) {
return
}
sink.addPresentation(InlineInlayPosition(pos, belongsToBefore), hasBackground = true) {
Expand Down
81 changes: 53 additions & 28 deletions src/main/kotlin/de/sirywell/handlehints/dfa/SsaAnalyzer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -83,19 +83,18 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
is PsiSwitchLabeledRuleStatement -> element.enclosingSwitchBlock?.expression ?: return
else -> TODO("Not supported: ${element.javaClass}")
}
val type = typeData<MethodHandleType>(expression) ?: resolveType(expression, block)
val type = typeData(expression) ?: resolveType(expression, block)
type?.let { typeData[expression] = it }
ssaConstruction.writeVariable(instruction.variable, block, Holder(type ?: notConstant()))
if (type != null) {
typeData[controlFlow.getElement(index)] = type
}
ssaConstruction.writeVariable(instruction.variable, block, Holder(type ?: return))
typeData[controlFlow.getElement(index)] = type
}

/**
* Returns true if the variable type has no [MethodHandleType]
*/
private fun isUnrelated(variable: PsiVariable): Boolean {
return isUnrelated(variable.type, variable) }
return isUnrelated(variable.type, variable)
}

private fun isUnrelated(type: PsiType, context: PsiElement): Boolean {
return type != methodTypeType(context)
Expand Down Expand Up @@ -160,23 +159,30 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
}

"appendParameterTypes" ->
methodTypeHelper.appendParameterTypes(qualifier?.methodHandleType(block) ?: return noMatch(), arguments)
methodTypeHelper.appendParameterTypes(
qualifier?.methodHandleType(block) ?: return noMatch(),
arguments
)

"erase" ->
methodTypeHelper.erase(qualifier?.methodHandleType(block) ?: return noMatch(), expression)

"generic" ->
methodTypeHelper.generic(qualifier?.methodHandleType(block) ?: return noMatch(), objectType(expression))
methodTypeHelper.generic(
qualifier?.methodHandleType(block) ?: return noMatch(),
objectType(expression)
)

"genericMethodType" -> {
val size = arguments.size
if (size != 1 && arguments.size != 2) return noMatch()
val finalArray =
if (size == 1) false else arguments[1].getConstantOfType<Boolean>() ?: return notConstant()
if (size == 1) false else arguments[1].getConstantOfType<Boolean>()
?: return notConstant<MethodHandleType>()
methodTypeHelper.genericMethodType(arguments[0], finalArray, objectType(expression))
}

"fromMethodDescriptorString" -> notConstant() // not supported
"fromMethodDescriptorString" -> notConstant<MethodHandleType>() // not supported

"describeConstable",
"descriptorString",
Expand All @@ -200,8 +206,9 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
"asCollector" -> TODO()
"asFixedArity" -> {
if (arguments.isNotEmpty()) return noMatch()
methodHandleTransformer.asFixedArity(qualifier?.methodHandleType(block)?: notConstant())
methodHandleTransformer.asFixedArity(qualifier?.methodHandleType(block) ?: notConstant())
}

"asSpreader" -> TODO()
"asType" -> TODO()
"asVarargsCollector" -> TODO()
Expand All @@ -216,9 +223,9 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
"invoke",
"invokeExact",
"invokeWithArguments" -> noMethodHandle()

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

in objectMethods -> noMethodHandle()
else -> warnUnsupported(expression, "MethodHandle")
Expand All @@ -239,7 +246,7 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
}
}
// TODO is there a better way for this?
val resolver = HandleTypeResolver(this, block, MethodHandleType(BotSignature), MethodHandleType::class)
val resolver = HandleTypeResolver(this, block, BotMethodHandleType, MethodHandleType::class)
val resolver2 = HandleTypeResolver(this, block, BotVarHandleType, VarHandleType::class)
expression.accept(resolver)
expression.accept(resolver2)
Expand All @@ -262,14 +269,14 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
"findSpecial" -> {
if (arguments.size != 4) return noMatch()
val (refc, name, type, specialCaller) = arguments
val t = type.methodHandleType(block) ?: return notConstant()
val t = type.methodHandleType(block) ?: notConstant()
lookupHelper.findSpecial(refc, name, t, specialCaller)
}

"findStatic" -> {
if (arguments.size != 3) return noMatch()
val (refc, name, type) = arguments
val t = type.methodHandleType(block) ?: return notConstant()
val t = type.methodHandleType(block) ?: notConstant()
lookupHelper.findStatic(refc, name, t)
}

Expand All @@ -278,9 +285,10 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
"findVirtual" -> {
if (arguments.size != 3) return noMatch()
val (refc, name, type) = arguments
val t = type.methodHandleType(block) ?: return notConstant()
val t = type.methodHandleType(block) ?: notConstant()
lookupHelper.findVirtual(refc, name, t)
}

"findStaticVarHandle" -> findAccessor(arguments, lookupHelper::findStaticVarHandle)
"findVarHandle" -> findAccessor(arguments, lookupHelper::findVarHandle)

Expand Down Expand Up @@ -364,11 +372,11 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)

"dropArguments" -> {
if (arguments.size < 3) return noMatch()
val i = arguments[1].getConstantOfType<Int>() ?: return notConstant()
val i = arguments[1].getConstantOfType<Int>() ?: return notConstant<MethodHandleType>()
methodHandlesMerger.dropArguments(arguments[0], i, arguments.subList(2), block)
}

"dropArgumentsToMatch" -> notConstant() // likely too difficult to get something precise here
"dropArgumentsToMatch" -> notConstant<MethodHandleType>() // likely too difficult to get something precise here
"dropReturn" -> {
if (arguments.size != 1) return noMatch()
methodHandlesMerger.dropReturn(arguments[0], block)
Expand All @@ -395,7 +403,7 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
"filterArguments" -> {
if (arguments.size < 2) return noMatch()
val target = arguments[0].methodHandleType(block) ?: return noMatch()
val pos = arguments[1].getConstantOfType<Int>() ?: return notConstant()
val pos = arguments[1].getConstantOfType<Int>() ?: return notConstant<MethodHandleType>()
val filter = arguments.drop(2).map { it.methodHandleType(block) ?: return noMatch() }
methodHandlesMerger.filterArguments(target, pos, filter)
}
Expand All @@ -412,7 +420,7 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
when (arguments.size) {
3 -> {
target = arguments[0].methodHandleType(block) ?: return noMatch()
pos = arguments[1].getConstantOfType<Int>() ?: return notConstant()
pos = arguments[1].getConstantOfType<Int>() ?: return notConstant<MethodHandleType>()
combiner = arguments[2].methodHandleType(block) ?: return noMatch()
}

Expand Down Expand Up @@ -456,7 +464,7 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
}

"iteratedLoop" -> TODO("not implemented")
"loop" -> notConstant()
"loop" -> notConstant<MethodHandleType>()
"permuteArguments" -> {
if (arguments.size < 2) noMatch()
else {
Expand All @@ -471,7 +479,7 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)

"spreadInvoker" -> {
if (arguments.size != 2) return noMatch()
val leadingArgCount = arguments[1].getConstantOfType<Int>() ?: return notConstant()
val leadingArgCount = arguments[1].getConstantOfType<Int>() ?: return notConstant<MethodHandleType>()
methodHandlesInitializer.spreadInvoker(
arguments[0].methodHandleType(block) ?: return noMatch(),
leadingArgCount,
Expand Down Expand Up @@ -524,12 +532,12 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
}
}

private fun <T: TypeLatticeElement<*>> noMatch(): T? {
private fun <T : TypeLatticeElement<*>> noMatch(): T? {
return null
}

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

private fun singleParameter(
Expand Down Expand Up @@ -574,8 +582,25 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
return PsiType.getJavaLangObject(element.manager, element.resolveScope)
}

private fun warnUnsupported(expression: PsiMethodCallExpression, className: String): MethodHandleType {
private fun warnUnsupported(
expression: PsiMethodCallExpression,
className: String
): TypeLatticeElement<*>? {
LOG.warnOnce("Unsupported method $className#${expression.methodName}")
return MethodHandleType(BotSignature)
return null
}

private inline fun <reified T> bottomForType(): T {
if (T::class == MethodHandleType::class) {
return T::class.java.cast(BotMethodHandleType)
}
if (T::class == VarHandleType::class) {
return T::class.java.cast(BotVarHandleType)
}
if (T::class == Type::class) {
return T::class.java.cast(BotType)
}
throw UnsupportedOperationException("${T::class} is not supported")
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ class MethodHandleInvokeInspection : LocalInspectionTool() {
val target = expression.methodExpression.qualifierExpression ?: return
val typeData = TypeData.forFile(expression.containingFile)
val type = typeData[target] as? MethodHandleType ?: return
if (type.signature !is CompleteSignature) return // ignore for now
val (returnType, parameters) = type.signature
if (type !is CompleteMethodHandleType) return // ignore for now
val (returnType, parameters) = type
when (expression.methodName) {
"invoke" -> {
if (type.signature.varargs == TriState.NO) {
if (type.varargs == TriState.NO) {
checkArgumentsCount(parameters, expression)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.intellij.psi.PsiTypes
import de.sirywell.handlehints.MethodHandleBundle.message
import de.sirywell.handlehints.TypeData
import de.sirywell.handlehints.type.MethodHandleType
import de.sirywell.handlehints.type.TopSignature
import de.sirywell.handlehints.type.TopMethodHandleType
import de.sirywell.handlehints.type.Type
import org.jetbrains.annotations.Nls

Expand All @@ -17,7 +17,7 @@ abstract class ProblemEmitter(private val typeData: TypeData) {
typeData.reportProblem(element) {
it.registerProblem(element, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
}
return MethodHandleType(TopSignature)
return TopMethodHandleType
}

protected fun emitMustNotBeVoid(typeExpr: PsiExpression) {
Expand Down
47 changes: 20 additions & 27 deletions src/main/kotlin/de/sirywell/handlehints/mhtype/LookupHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,34 @@ class LookupHelper(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter(ssaAna
// MethodHandle factory methods

fun findConstructor(refc: PsiExpression, typeExpr: PsiExpression, block: SsaConstruction.Block): MethodHandleType {
val type = ssaAnalyzer.methodHandleType(typeExpr, block) ?: return MethodHandleType(BotSignature)
if (!type.signature.returnType.canBe(PsiTypes.voidType())) {
val type = ssaAnalyzer.methodHandleType(typeExpr, block) ?: return BotMethodHandleType
if (!type.returnType.canBe(PsiTypes.voidType())) {
emitProblem(
typeExpr,
MethodHandleBundle.message(
"problem.merging.general.otherReturnTypeExpected",
type.signature.returnType,
type.returnType,
PsiTypes.voidType().presentableText
)
)
}
val referenceClass = refc.asReferenceType()
val methodHandleType = enhanceVarargsIfKnown(referenceClass, type.signature) { it.constructors }
return MethodHandleType(
methodHandleType
.signature.withReturnType(referenceClass)
.withVarargs(methodHandleType.signature.varargs)
)
val methodHandleType = enhanceVarargsIfKnown(referenceClass, type) { it.constructors }
return methodHandleType
.withReturnType(referenceClass)
.withVarargs(methodHandleType.varargs)
}

fun findGetter(refc: PsiExpression, typeExpr: PsiExpression): MethodHandleType {
val referenceClass = refc.asReferenceType()
val returnType = typeExpr.asNonVoidType()
return MethodHandleType(complete(returnType, listOf(referenceClass)))
return complete(returnType, listOf(referenceClass))
}

fun findSetter(refc: PsiExpression, typeExpr: PsiExpression): MethodHandleType {
val referenceClass = refc.asReferenceType()
val paramType = typeExpr.asNonVoidType()
return MethodHandleType(complete(ExactType.voidType, listOf(referenceClass, paramType)))
return complete(ExactType.voidType, listOf(referenceClass, paramType))
}

fun findSpecial(
Expand All @@ -55,41 +53,39 @@ class LookupHelper(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter(ssaAna
specialCaller: PsiExpression
): MethodHandleType {
val referenceClass = refc.asReferenceType()
if (type.signature !is CompleteSignature) return type
// TODO inspection: caller class must be a subclass below the method
val paramType = specialCaller.asType()
val name = nameExpr.getConstantOfType<String>() ?: return prependParameter(type, paramType)
return prependParameter(enhanceVarargsIfKnown(referenceClass, type.signature) {
return prependParameter(enhanceVarargsIfKnown(referenceClass, type) {
it.findMethodsByName(name, true)
}, paramType)
}

fun findStatic(refc: PsiExpression, nameExpr: PsiExpression, mhType: MethodHandleType): MethodHandleType {
val type = refc.asReferenceType()
val name = nameExpr.getConstantOfType<String>() ?: return mhType
return enhanceVarargsIfKnown(type, mhType.signature) {
return enhanceVarargsIfKnown(type, mhType) {
it.findMethodsByName(name, true)
}
}

fun findStaticGetter(refc: PsiExpression, type: PsiExpression): MethodHandleType {
refc.asReferenceType()
val returnType = type.asNonVoidType()
return MethodHandleType(complete(returnType, listOf()))
return complete(returnType, listOf())
}

fun findStaticSetter(refc: PsiExpression, type: PsiExpression): MethodHandleType {
refc.asReferenceType()
val paramType = type.asNonVoidType()
return MethodHandleType(complete(ExactType.voidType, listOf(paramType)))
return complete(ExactType.voidType, listOf(paramType))
}

fun findVirtual(refc: PsiExpression, nameExpr: PsiExpression, type: MethodHandleType): MethodHandleType {
val referenceClass = refc.asReferenceType()
if (type.signature !is CompleteSignature) return type
// TODO not exactly correct, receiver could be restricted to lookup class
val name = nameExpr.getConstantOfType<String>() ?: return prependParameter(type, referenceClass)
return prependParameter(enhanceVarargsIfKnown(referenceClass, type.signature) {
return prependParameter(enhanceVarargsIfKnown(referenceClass, type) {
it.findMethodsByName(name, true)
}, referenceClass)
}
Expand All @@ -98,11 +94,8 @@ class LookupHelper(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter(ssaAna
mhType: MethodHandleType,
paramType: Type
): MethodHandleType {
if (mhType.signature !is CompleteSignature) {
return mhType
}
val pt = CompleteParameterList(listOf(paramType)).addAllAt(1, mhType.signature.parameterList)
return MethodHandleType(mhType.signature.withParameterTypes(pt).withVarargs(mhType.signature.varargs))
val pt = CompleteParameterList(listOf(paramType)).addAllAt(1, mhType.parameterList)
return mhType.withParameterTypes(pt).withVarargs(mhType.varargs)
}

private fun PsiExpression.asReferenceType(): Type {
Expand All @@ -126,17 +119,17 @@ class LookupHelper(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter(ssaAna

private fun enhanceVarargsIfKnown(
referenceClass: Type,
signature: Signature,
methodHandleType: MethodHandleType,
methods: (PsiClass) -> Array<PsiMethod>
): MethodHandleType {
if (referenceClass is ExactType) {
val method = PsiTypesUtil.getPsiClass(referenceClass.psiType)?.let {
findMethodMatching(signature, methods(it))
findMethodMatching(methodHandleType, methods(it))
}
val varargs = method?.isVarArgs.toTriState()
return MethodHandleType(signature.withVarargs(varargs))
return methodHandleType.withVarargs(varargs)
}
return MethodHandleType(signature)
return methodHandleType
}

fun findStaticVarHandle(declExpr: PsiExpression, typeExpr: PsiExpression): VarHandleType {
Expand Down
Loading

0 comments on commit 2222c27

Please sign in to comment.