From 40a7b6f437ea1f88a357ad4375eacc458237bd36 Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Sat, 19 Sep 2026 18:36:07 +0100 Subject: [PATCH] Fix: Handle quantifiers and staticness properly when resolving Mixin target methods. We should only take as many matches as is allowed, and additionally static methods should not be considered unless our handler is also static or we are trying to select exactly 1 method. --- .../handlers/InjectorAnnotationHandler.kt | 24 +++++++++---------- .../injectionPoint/NewInsnInjectionPoint.kt | 2 +- .../reference/AbstractMethodReference.kt | 16 +++++++++---- .../kotlin/platform/mixin/util/AsmUtil.kt | 6 +++-- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/platform/mixin/handlers/InjectorAnnotationHandler.kt b/src/main/kotlin/platform/mixin/handlers/InjectorAnnotationHandler.kt index 8b6c252d3..c4acbf57b 100644 --- a/src/main/kotlin/platform/mixin/handlers/InjectorAnnotationHandler.kt +++ b/src/main/kotlin/platform/mixin/handlers/InjectorAnnotationHandler.kt @@ -31,6 +31,7 @@ import com.demonwav.mcdev.platform.mixin.reference.parseMixinSelector import com.demonwav.mcdev.platform.mixin.util.ClassAndMethodNode import com.demonwav.mcdev.platform.mixin.util.MethodTargetMember import com.demonwav.mcdev.platform.mixin.util.MixinTargetMember +import com.demonwav.mcdev.platform.mixin.util.findMethods import com.demonwav.mcdev.platform.mixin.util.getGenericParameterTypes import com.demonwav.mcdev.platform.mixin.util.hasAccess import com.demonwav.mcdev.platform.mixin.util.mixinTargets @@ -46,8 +47,10 @@ import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiElement import com.intellij.psi.PsiEllipsisType import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiModifier import com.intellij.psi.PsiType import com.intellij.psi.util.PsiModificationTracker +import com.intellij.psi.util.findParentOfType import com.llamalad7.mixinextras.expression.impl.point.ExpressionContext import java.util.concurrent.ConcurrentHashMap import org.objectweb.asm.Opcodes @@ -63,21 +66,18 @@ abstract class InjectorAnnotationHandler : MixinAnnotationHandler { val selectors = method.mapNotNull { parseMixinSelector(it, methodAttr!!) } + desc.mapNotNull { DescSelectorParser.Util.descSelectorFromAnnotation(it) } - val targetClassMethods = selectors.associateWith { selector -> - val actualTarget = selector.getCustomOwner(targetClass) - (actualTarget to actualTarget.methods) + val targetsBySelector = selectors.associateWith { selector -> + selector.getCustomOwner(targetClass) } + val isStatic = annotation.findParentOfType()?.hasModifierProperty(PsiModifier.STATIC) ?: true - return targetClassMethods.flatMap { (selector, pair) -> - val (clazz, methods) = pair - methods.mapNotNull { method -> - if (selector.matchMethod(method, clazz)) { - MethodTargetMember(clazz, method) - } else { - null - } + return targetsBySelector.asSequence() + .flatMap { (selector, targetClass) -> + targetClass.findMethods(selector, allowStatic = isStatic) + .map { MethodTargetMember(targetClass, it) } } - } + .distinctBy { it.classAndMethod } + .toList() } override fun isUnresolved(annotation: PsiAnnotation, targetClass: ClassNode): InsnResolutionInfo.Failure? { diff --git a/src/main/kotlin/platform/mixin/handlers/injectionPoint/NewInsnInjectionPoint.kt b/src/main/kotlin/platform/mixin/handlers/injectionPoint/NewInsnInjectionPoint.kt index 185bccac5..6b18fd55d 100644 --- a/src/main/kotlin/platform/mixin/handlers/injectionPoint/NewInsnInjectionPoint.kt +++ b/src/main/kotlin/platform/mixin/handlers/injectionPoint/NewInsnInjectionPoint.kt @@ -143,7 +143,7 @@ class NewInsnInjectionPoint : InjectionPoint() { val anonymousName = anonymousClass?.fullQualifiedName?.replace('.', '/') if (anonymousName != null) { val methods = findClassNodeByPsiClass(anonymousClass) - ?.findMethods(selector.withQuantifier(Quantifier.Default)) + ?.findMethods(selector.withQuantifier(Quantifier.Any), allowStatic = true) .orEmpty() if (methods.any { selector.matchMethod(anonymousName, it.name, it.desc) }) { diff --git a/src/main/kotlin/platform/mixin/reference/AbstractMethodReference.kt b/src/main/kotlin/platform/mixin/reference/AbstractMethodReference.kt index 870f54ee1..c10f6f1e9 100644 --- a/src/main/kotlin/platform/mixin/reference/AbstractMethodReference.kt +++ b/src/main/kotlin/platform/mixin/reference/AbstractMethodReference.kt @@ -48,6 +48,8 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiArrayInitializerMemberValue import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiModifier import com.intellij.psi.PsiSubstitutor import com.intellij.psi.ResolveResult import com.intellij.psi.util.parentOfType @@ -82,13 +84,15 @@ abstract class AbstractMethodReference : PolyReferenceResolver(), MixinReference return false } + val isStatic = context.parentOfType()?.hasModifierProperty(PsiModifier.STATIC) ?: true val stringValue = context.constantStringValue ?: return false val targetMethodInfo = parseSelector(stringValue, context) ?: return false val minMatches = targetMethodInfo.quantifier.min(Quantifier.Context.MEMBER).coerceAtLeast(1) val targets = getTargets(context) ?: return false return targets.any { - targetMethodInfo.getCustomOwner(it).findMethods(targetMethodInfo).countIsLessThan(minMatches) + targetMethodInfo.getCustomOwner(it).findMethods(targetMethodInfo, allowStatic = isStatic) + .countIsLessThan(minMatches) } } @@ -104,10 +108,13 @@ abstract class AbstractMethodReference : PolyReferenceResolver(), MixinReference } private fun isAmbiguous(targets: Collection, targetReference: MemberInfo): Boolean { - return targets.any { it.findMethods(targetReference.withQuantifier(Quantifier.Any)).countIsAtLeast(2) } + return targets.any { + it.findMethods(targetReference.withQuantifier(Quantifier.Any), allowStatic = true).countIsAtLeast(2) + } } fun resolve(context: PsiElement): Sequence? { + val isStatic = context.parentOfType()?.hasModifierProperty(PsiModifier.STATIC) ?: true val targets = getTargets(context) ?: return null val targetedMethods = when (context) { is PsiArrayInitializerMemberValue -> context.initializers.mapNotNull { it.constantStringValue } @@ -116,18 +123,19 @@ abstract class AbstractMethodReference : PolyReferenceResolver(), MixinReference return targetedMethods.asSequence().flatMap { method -> val targetReference = parseSelector(method, context) ?: return@flatMap emptySequence() - return@flatMap resolve(targets, targetReference) + return@flatMap resolve(targets, targetReference, allowStatic = isStatic) } } private fun resolve( targets: Collection, selector: MixinSelector, + allowStatic: Boolean, ): Sequence { return targets.asSequence() .flatMap { target -> val actualTarget = selector.getCustomOwner(target) - actualTarget.findMethods(selector).map { ClassAndMethodNode(actualTarget, it) } + actualTarget.findMethods(selector, allowStatic).map { ClassAndMethodNode(actualTarget, it) } } } diff --git a/src/main/kotlin/platform/mixin/util/AsmUtil.kt b/src/main/kotlin/platform/mixin/util/AsmUtil.kt index f1a1407e4..80e768b67 100644 --- a/src/main/kotlin/platform/mixin/util/AsmUtil.kt +++ b/src/main/kotlin/platform/mixin/util/AsmUtil.kt @@ -472,9 +472,11 @@ fun ClassNode.findField(ref: MemberMatcher): FieldNode? { return findFields(ref).firstOrNull() } -fun ClassNode.findMethods(ref: MixinSelector): Sequence { +fun ClassNode.findMethods(ref: MixinSelector, allowStatic: Boolean): Sequence { val maxMatches = ref.quantifier.max(Quantifier.Context.MEMBER) - return methods?.asSequence()?.filter { ref.matchMethod(it, this) }?.take(maxMatches).orEmpty() + return methods?.asSequence()?.filter { + ref.matchMethod(it, this) && (maxMatches <= 1 || allowStatic || !it.hasAccess(Opcodes.ACC_STATIC)) + }?.take(maxMatches).orEmpty() } fun ClassNode.findMethod(ref: MemberReference): MethodNode? {