Skip to content

Commit 739fc17

Browse files
authored
Fix: Handle quantifiers and staticness properly when resolving Mixin target methods. (#2645)
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.
1 parent 3891c17 commit 739fc17

4 files changed

Lines changed: 30 additions & 19 deletions

File tree

‎src/main/kotlin/platform/mixin/handlers/InjectorAnnotationHandler.kt‎

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.demonwav.mcdev.platform.mixin.reference.parseMixinSelector
3131
import com.demonwav.mcdev.platform.mixin.util.ClassAndMethodNode
3232
import com.demonwav.mcdev.platform.mixin.util.MethodTargetMember
3333
import com.demonwav.mcdev.platform.mixin.util.MixinTargetMember
34+
import com.demonwav.mcdev.platform.mixin.util.findMethods
3435
import com.demonwav.mcdev.platform.mixin.util.getGenericParameterTypes
3536
import com.demonwav.mcdev.platform.mixin.util.hasAccess
3637
import com.demonwav.mcdev.platform.mixin.util.mixinTargets
@@ -46,8 +47,10 @@ import com.intellij.psi.PsiAnnotation
4647
import com.intellij.psi.PsiElement
4748
import com.intellij.psi.PsiEllipsisType
4849
import com.intellij.psi.PsiMethod
50+
import com.intellij.psi.PsiModifier
4951
import com.intellij.psi.PsiType
5052
import com.intellij.psi.util.PsiModificationTracker
53+
import com.intellij.psi.util.findParentOfType
5154
import com.llamalad7.mixinextras.expression.impl.point.ExpressionContext
5255
import java.util.concurrent.ConcurrentHashMap
5356
import org.objectweb.asm.Opcodes
@@ -63,21 +66,19 @@ abstract class InjectorAnnotationHandler : MixinAnnotationHandler {
6366
val selectors = method.mapNotNull { parseMixinSelector(it, methodAttr!!) } +
6467
desc.mapNotNull { DescSelectorParser.Util.descSelectorFromAnnotation(it) }
6568

66-
val targetClassMethods = selectors.associateWith { selector ->
67-
val actualTarget = selector.getCustomOwner(targetClass)
68-
(actualTarget to actualTarget.methods)
69+
val targetsBySelector = selectors.associateWith { selector ->
70+
selector.getCustomOwner(targetClass)
6971
}
72+
val allowStatic = annotation.findParentOfType<PsiMethod>()?.hasModifierProperty(PsiModifier.STATIC) ?: true
7073

71-
return targetClassMethods.flatMap { (selector, pair) ->
72-
val (clazz, methods) = pair
73-
methods.mapNotNull { method ->
74-
if (selector.matchMethod(method, clazz)) {
75-
MethodTargetMember(clazz, method)
76-
} else {
77-
null
78-
}
74+
return targetsBySelector.asSequence()
75+
.flatMap { (selector, targetClass) ->
76+
targetClass.findMethods(selector, allowStatic)
77+
.map { ClassAndMethodNode(targetClass, it) }
7978
}
80-
}
79+
.distinct()
80+
.map { MethodTargetMember(it) }
81+
.toList()
8182
}
8283

8384
override fun isUnresolved(annotation: PsiAnnotation, targetClass: ClassNode): InsnResolutionInfo.Failure? {

‎src/main/kotlin/platform/mixin/handlers/injectionPoint/NewInsnInjectionPoint.kt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ class NewInsnInjectionPoint : InjectionPoint<PsiMember>() {
143143
val anonymousName = anonymousClass?.fullQualifiedName?.replace('.', '/')
144144
if (anonymousName != null) {
145145
val methods = findClassNodeByPsiClass(anonymousClass)
146-
?.findMethods(selector.withQuantifier(Quantifier.Default))
146+
?.findMethods(selector.withQuantifier(Quantifier.Any), allowStatic = true)
147147
.orEmpty()
148148

149149
if (methods.any { selector.matchMethod(anonymousName, it.name, it.desc) }) {

‎src/main/kotlin/platform/mixin/reference/AbstractMethodReference.kt‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder
4848
import com.intellij.psi.PsiAnnotation
4949
import com.intellij.psi.PsiArrayInitializerMemberValue
5050
import com.intellij.psi.PsiElement
51+
import com.intellij.psi.PsiMethod
52+
import com.intellij.psi.PsiModifier
5153
import com.intellij.psi.PsiSubstitutor
5254
import com.intellij.psi.ResolveResult
5355
import com.intellij.psi.util.parentOfType
@@ -82,13 +84,15 @@ abstract class AbstractMethodReference : PolyReferenceResolver(), MixinReference
8284
return false
8385
}
8486

87+
val allowStatic = context.parentOfType<PsiMethod>()?.hasModifierProperty(PsiModifier.STATIC) ?: true
8588
val stringValue = context.constantStringValue ?: return false
8689
val targetMethodInfo = parseSelector(stringValue, context) ?: return false
8790
val minMatches = targetMethodInfo.quantifier.min(Quantifier.Context.MEMBER).coerceAtLeast(1)
8891
val targets = getTargets(context) ?: return false
8992

9093
return targets.any {
91-
targetMethodInfo.getCustomOwner(it).findMethods(targetMethodInfo).countIsLessThan(minMatches)
94+
targetMethodInfo.getCustomOwner(it).findMethods(targetMethodInfo, allowStatic)
95+
.countIsLessThan(minMatches)
9296
}
9397
}
9498

@@ -104,10 +108,13 @@ abstract class AbstractMethodReference : PolyReferenceResolver(), MixinReference
104108
}
105109

106110
private fun isAmbiguous(targets: Collection<ClassNode>, targetReference: MemberInfo): Boolean {
107-
return targets.any { it.findMethods(targetReference.withQuantifier(Quantifier.Any)).countIsAtLeast(2) }
111+
return targets.any {
112+
it.findMethods(targetReference.withQuantifier(Quantifier.Any), allowStatic = true).countIsAtLeast(2)
113+
}
108114
}
109115

110116
fun resolve(context: PsiElement): Sequence<ClassAndMethodNode>? {
117+
val allowStatic = context.parentOfType<PsiMethod>()?.hasModifierProperty(PsiModifier.STATIC) ?: true
111118
val targets = getTargets(context) ?: return null
112119
val targetedMethods = when (context) {
113120
is PsiArrayInitializerMemberValue -> context.initializers.mapNotNull { it.constantStringValue }
@@ -116,18 +123,19 @@ abstract class AbstractMethodReference : PolyReferenceResolver(), MixinReference
116123

117124
return targetedMethods.asSequence().flatMap { method ->
118125
val targetReference = parseSelector(method, context) ?: return@flatMap emptySequence()
119-
return@flatMap resolve(targets, targetReference)
126+
return@flatMap resolve(targets, targetReference, allowStatic)
120127
}
121128
}
122129

123130
private fun resolve(
124131
targets: Collection<ClassNode>,
125132
selector: MixinSelector,
133+
allowStatic: Boolean,
126134
): Sequence<ClassAndMethodNode> {
127135
return targets.asSequence()
128136
.flatMap { target ->
129137
val actualTarget = selector.getCustomOwner(target)
130-
actualTarget.findMethods(selector).map { ClassAndMethodNode(actualTarget, it) }
138+
actualTarget.findMethods(selector, allowStatic).map { ClassAndMethodNode(actualTarget, it) }
131139
}
132140
}
133141

‎src/main/kotlin/platform/mixin/util/AsmUtil.kt‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,9 +472,11 @@ fun ClassNode.findField(ref: MemberMatcher): FieldNode? {
472472
return findFields(ref).firstOrNull()
473473
}
474474

475-
fun ClassNode.findMethods(ref: MixinSelector): Sequence<MethodNode> {
475+
fun ClassNode.findMethods(ref: MixinSelector, allowStatic: Boolean): Sequence<MethodNode> {
476476
val maxMatches = ref.quantifier.max(Quantifier.Context.MEMBER)
477-
return methods?.asSequence()?.filter { ref.matchMethod(it, this) }?.take(maxMatches).orEmpty()
477+
return methods?.asSequence()?.filter {
478+
ref.matchMethod(it, this) && (maxMatches <= 1 || allowStatic || !it.hasAccess(Opcodes.ACC_STATIC))
479+
}?.take(maxMatches).orEmpty()
478480
}
479481

480482
fun ClassNode.findMethod(ref: MemberReference): MethodNode? {

0 commit comments

Comments
 (0)