diff --git a/build.gradle.kts b/build.gradle.kts index 99e7431fb..0e8cf8ae6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -102,7 +102,7 @@ dependencies { implementation(files(Jvm.current().toolsJar)) // TODO: temporary waiting for a release - fun mixinExtras(variant: String) = "com.github.LlamaLad7.MixinExtras:mixinextras-$variant:4d2e01e" + fun mixinExtras(variant: String) = "com.github.LlamaLad7.MixinExtras:mixinextras-$variant:2ad48e8" implementation(mixinExtras("expressions")) testLibs(mixinExtras("common")) diff --git a/src/main/kotlin/platform/mixin/completion/MixinCompletionConfidence.kt b/src/main/kotlin/platform/mixin/completion/MixinCompletionConfidence.kt index d8d5535a8..d3a5c35f0 100644 --- a/src/main/kotlin/platform/mixin/completion/MixinCompletionConfidence.kt +++ b/src/main/kotlin/platform/mixin/completion/MixinCompletionConfidence.kt @@ -39,6 +39,7 @@ class MixinCompletionConfidence : CompletionConfidence() { PsiJavaPatterns.psiAnnotation().qName( StandardPatterns.or( StandardPatterns.string().startsWith(MixinConstants.PACKAGE), + StandardPatterns.string().startsWith(MixinConstants.MixinExtras.PACKAGE), StandardPatterns.string() .oneOf(MixinAnnotationHandler.getBuiltinHandlers().map { it.first }.toList()), ) diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionAnnotator.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionAnnotator.kt index bcabbf930..638bf6f13 100644 --- a/src/main/kotlin/platform/mixin/expression/MEExpressionAnnotator.kt +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionAnnotator.kt @@ -40,16 +40,24 @@ import com.demonwav.mcdev.platform.mixin.expression.gen.psi.METype import com.demonwav.mcdev.platform.mixin.expression.psi.METypeUtil import com.demonwav.mcdev.platform.mixin.util.MixinConstants import com.demonwav.mcdev.util.findMultiInjectionHost +import com.intellij.codeInsight.AutoPopupController import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.RemoveAnnotationQuickFix +import com.intellij.lang.annotation.AnnotationBuilder import com.intellij.lang.annotation.AnnotationHolder import com.intellij.lang.annotation.Annotator import com.intellij.lang.annotation.HighlightSeverity +import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.project.Project import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile import com.intellij.psi.PsiModifierListOwner +import com.intellij.psi.impl.source.tree.injected.InjectedLanguageEditorUtil import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.util.TypeConversionUtil import com.intellij.psi.util.parentOfType @@ -275,6 +283,7 @@ class MEExpressionAnnotator : Annotator { ) .range(type) .highlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL) + .withDefinitionFix(type) .create() } else { holder.newSilentAnnotation(HighlightSeverity.TEXT_ATTRIBUTES) @@ -306,6 +315,7 @@ class MEExpressionAnnotator : Annotator { ) .range(variable) .highlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL) + .withDefinitionFix(variable) .create() } else { holder.newSilentAnnotation(HighlightSeverity.TEXT_ATTRIBUTES) @@ -314,4 +324,45 @@ class MEExpressionAnnotator : Annotator { .create() } } + + private fun AnnotationBuilder.withDefinitionFix(name: MEName) = + withFix(AddDefinitionInspection(name)) + + private class AddDefinitionInspection(name: MEName) : LocalQuickFixAndIntentionActionOnPsiElement(name) { + private val id = name.text + + override fun getFamilyName(): String = "Add @Definition" + + override fun getText(): String = "$familyName(id = \"$id\")" + + override fun invoke( + project: Project, + file: PsiFile, + editor: Editor?, + startElement: PsiElement, + endElement: PsiElement + ) { + if (editor == null) { + MEExpressionCompletionUtil.addDefinition( + project, + startElement, + id, + "" + ) + return + } + val annotation = MEExpressionCompletionUtil.addDefinition( + project, + startElement, + id, + "dummy" + ) ?: return + val dummy = annotation.findAttribute("dummy") as? PsiElement ?: return + val hostEditor = InjectedLanguageEditorUtil.getTopLevelEditor(editor) + hostEditor.caretModel.moveToOffset(dummy.textOffset) + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(hostEditor.document) + hostEditor.document.replaceString(dummy.textRange.startOffset, dummy.textRange.endOffset, "") + AutoPopupController.getInstance(project).autoPopupMemberLookup(hostEditor, null) + } + } } diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt index dcda90ba2..f87c86668 100644 --- a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt @@ -691,11 +691,12 @@ object MEExpressionCompletionUtil { canCompleteExprs: Boolean, canCompleteTypes: Boolean ): List { + val flow = flows[insn] when (insn.insn) { is LdcInsnNode -> { when (val cst = insn.insn.cst) { is Type -> { - if (canCompleteTypes && cst.isAccessibleFrom(mixinClass)) { + if (canCompleteExprs && cst.isAccessibleFrom(mixinClass)) { return listOf( createTypeLookup(cst) .withTailText(".class") @@ -727,7 +728,7 @@ object MEExpressionCompletionUtil { is FieldInsnNode -> { if (canCompleteExprs) { val definitionValue = "field = \"L${insn.insn.owner};${insn.insn.name}:${insn.insn.desc}\"" - var lookup = LookupElementBuilder.create(insn.insn.name.toValidIdentifier()) + var lookup = createUniqueLookup(insn.insn.name.toValidIdentifier()) .withIcon(PlatformIcons.FIELD_ICON) .withPresentableText(insn.insn.owner.substringAfterLast('/') + "." + insn.insn.name) .withTypeText(Type.getType(insn.insn.desc).presentableName()) @@ -743,12 +744,10 @@ object MEExpressionCompletionUtil { is MethodInsnNode -> { if (canCompleteExprs) { val definitionValue = "method = \"L${insn.insn.owner};${insn.insn.name}${insn.insn.desc}\"" - var lookup = LookupElementBuilder.create(insn.insn.name.toValidIdentifier()) + var lookup = createUniqueLookup(insn.insn.name.toValidIdentifier()) .withIcon(PlatformIcons.METHOD_ICON) .withPresentableText(insn.insn.owner.substringAfterLast('/') + "." + insn.insn.name) - .withTailText( - "(" + Type.getArgumentTypes(insn.insn.desc).joinToString { it.presentableName() } + ")" - ) + .withDescTailText(insn.insn.desc) .withTypeText(Type.getReturnType(insn.insn.desc).presentableName()) .withDefinitionAndFold(insn.insn.name.toValidIdentifier(), "method", definitionValue) if (insn.insn.opcode == Opcodes.INVOKESTATIC) { @@ -766,25 +765,21 @@ object MEExpressionCompletionUtil { val lookup = createTypeLookup(type) when (insn.insn.opcode) { Opcodes.ANEWARRAY -> { - return listOf( - lookup.withTail( - BracketsTailType( - 1, - flows[insn]?.hasDecoration(FlowDecorations.ARRAY_CREATION_INFO) == true, - ) - ) - .createEliminable("new [${insn.insn.desc}") - ) + val arrayType = Type.getType('[' + Type.getObjectType(insn.insn.desc).descriptor) + return createNewArrayCompletion(flow, arrayType) } Opcodes.NEW -> { - val initCall = flows[insn] + val initCall = flow ?.getDecoration(FlowDecorations.INSTANTIATION_INFO) ?.initCall ?.virtualInsnOrNull - ?.insn as MethodInsnNode? + ?.insn as? MethodInsnNode + ?: return emptyList() return listOf( - lookup.withTail(ParenthesesTailType(initCall?.desc?.startsWith("()") == false)) - .createEliminable("new ${insn.insn.desc}${initCall?.desc}") + lookup + .withDescTailText(initCall.desc) + .withTail(ParenthesesTailType(!initCall.desc.startsWith("()"))) + .createEliminable("new ${insn.insn.desc}${initCall.desc}") ) } else -> return listOf(lookup.createEliminable("type ${insn.insn.desc}")) @@ -794,44 +789,27 @@ object MEExpressionCompletionUtil { is IntInsnNode -> { if (insn.insn.opcode == Opcodes.NEWARRAY) { if (canCompleteTypes) { - val type = when (insn.insn.operand) { - Opcodes.T_BOOLEAN -> "boolean" - Opcodes.T_CHAR -> "char" - Opcodes.T_FLOAT -> "float" - Opcodes.T_DOUBLE -> "double" - Opcodes.T_BYTE -> "byte" - Opcodes.T_SHORT -> "short" - Opcodes.T_INT -> "int" - Opcodes.T_LONG -> "long" - else -> "unknown" // wtf? - } - return listOf( - LookupElementBuilder.create(type) - .withIcon(PlatformIcons.CLASS_ICON) - .withTail( - BracketsTailType( - 1, - flows[insn]?.hasDecoration(FlowDecorations.ARRAY_CREATION_INFO) == true, - ) - ) - .createEliminable("new $type[]") + val arrayType = Type.getType( + when (insn.insn.operand) { + Opcodes.T_BOOLEAN -> "[B" + Opcodes.T_CHAR -> "[C" + Opcodes.T_FLOAT -> "[F" + Opcodes.T_DOUBLE -> "[D" + Opcodes.T_BYTE -> "[B" + Opcodes.T_SHORT -> "[S" + Opcodes.T_INT -> "[I" + Opcodes.T_LONG -> "[J" + else -> "[Lnull;" // wtf? + } ) + return createNewArrayCompletion(flow, arrayType) } } } is MultiANewArrayInsnNode -> { if (canCompleteTypes) { - val type = Type.getType(insn.insn.desc) - return listOf( - createTypeLookup(type.elementType) - .withTail( - BracketsTailType( - type.dimensions, - flows[insn]?.hasDecoration(FlowDecorations.ARRAY_CREATION_INFO) == true - ) - ) - .createEliminable("new ${insn.insn.desc}") - ) + val arrayType = Type.getType(insn.insn.desc) + return createNewArrayCompletion(flow, arrayType) } } is InsnNode -> { @@ -839,7 +817,7 @@ object MEExpressionCompletionUtil { Opcodes.ARRAYLENGTH -> { if (canCompleteExprs) { return listOf( - LookupElementBuilder.create("length") + createUniqueLookup("length") .withIcon(PlatformIcons.FIELD_ICON) .withTypeText("int") .createEliminable("arraylength") @@ -868,7 +846,7 @@ object MEExpressionCompletionUtil { ) } else { return listOf( - LookupElementBuilder.create(handle.name.toValidIdentifier()) + createUniqueLookup(handle.name.toValidIdentifier()) .withIcon(PlatformIcons.METHOD_ICON) .withPresentableText(handle.owner.substringAfterLast('/') + "." + handle.name) .withTypeText(Type.getReturnType(handle.desc).presentableName()) @@ -935,7 +913,7 @@ object MEExpressionCompletionUtil { private fun createTypeLookup(type: Type): LookupElementBuilder { val definitionId = type.typeNameToInsert() - val lookupElement = LookupElementBuilder.create(definitionId) + val lookupElement = createUniqueLookup(definitionId) .withIcon(PlatformIcons.CLASS_ICON) .withPresentableText(type.presentableName()) @@ -946,6 +924,22 @@ object MEExpressionCompletionUtil { } } + private fun createNewArrayCompletion(flow: FlowValue?, arrayType: Type): List { + val hasInitializer = flow?.hasDecoration(FlowDecorations.ARRAY_CREATION_INFO) == true + val initializerText = if (hasInitializer) "{}" else "" + return listOf( + createTypeLookup(arrayType.elementType) + .withTailText("[]".repeat(arrayType.dimensions) + initializerText) + .withTail( + BracketsTailType( + arrayType.dimensions, + hasInitializer, + ) + ) + .createEliminable("new ${arrayType.descriptor}$initializerText") + ) + } + private fun createLocalVariableLookups( project: Project, targetClass: ClassNode, @@ -997,7 +991,7 @@ object MEExpressionCompletionUtil { val ordinal = localsOfMyType.indexOf(localVariable) val isImplicit = localsOfMyType.size == 1 val localName = localVariable.name.toValidIdentifier() - LookupElementBuilder.create(localName) + createUniqueLookup(localName) .withIcon(PlatformIcons.VARIABLE_ICON) .withTypeText(localPsiType.presentableText) .withLocalDefinition( @@ -1020,7 +1014,7 @@ object MEExpressionCompletionUtil { val localName = localType.typeNameToInsert().replace("[]", "Array") + (ordinal + 1) val isImplicit = localTypes.count { it == localType } == 1 return listOf( - LookupElementBuilder.create(localName) + createUniqueLookup(localName) .withIcon(PlatformIcons.VARIABLE_ICON) .withTypeText(localType.presentableName()) .withLocalDefinition(localName, localType, ordinal, isArgsOnly, isImplicit, mixinClass) @@ -1028,6 +1022,11 @@ object MEExpressionCompletionUtil { ) } + private fun LookupElementBuilder.withDescTailText(desc: String) = + withTailText( + Type.getArgumentTypes(desc).joinToString(prefix = "(", postfix = ")") { it.presentableName() } + ) + private fun LookupElement.withTail(tailType: TailType?) = object : TailTypeDecorator(this) { override fun computeTailType(context: InsertionContext?) = tailType } @@ -1179,6 +1178,10 @@ object MEExpressionCompletionUtil { private fun addDefinition(context: InsertionContext, id: String, definitionValue: String): PsiAnnotation? { val contextElement = context.file.findElementAt(context.startOffset) ?: return null + return addDefinition(context.project, contextElement, id, definitionValue) + } + + fun addDefinition(project: Project, contextElement: PsiElement, id: String, definitionValue: String): PsiAnnotation? { val injectionHost = contextElement.findMultiInjectionHost() ?: return null val expressionAnnotation = injectionHost.parentOfType() ?: return null if (!expressionAnnotation.hasQualifiedName(MixinConstants.MixinExtras.EXPRESSION)) { @@ -1196,14 +1199,14 @@ object MEExpressionCompletionUtil { } // create and add the new @Definition annotation - var newAnnotation = JavaPsiFacade.getElementFactory(context.project).createAnnotationFromText( + var newAnnotation = JavaPsiFacade.getElementFactory(project).createAnnotationFromText( "@${MixinConstants.MixinExtras.DEFINITION}(id = \"$id\", $definitionValue)", modifierList, ) var anchor = modifierList.annotations.lastOrNull { it.hasQualifiedName(MixinConstants.MixinExtras.DEFINITION) } if (anchor == null) { val definitionPosRelativeToExpression = - MinecraftProjectSettings.getInstance(context.project).definitionPosRelativeToExpression + MinecraftProjectSettings.getInstance(project).definitionPosRelativeToExpression if (definitionPosRelativeToExpression == BeforeOrAfter.AFTER) { anchor = expressionAnnotation } @@ -1212,11 +1215,11 @@ object MEExpressionCompletionUtil { // add imports and reformat newAnnotation = - JavaCodeStyleManager.getInstance(context.project).shortenClassReferences(newAnnotation) as PsiAnnotation - JavaCodeStyleManager.getInstance(context.project).optimizeImports(modifierList.containingFile) + JavaCodeStyleManager.getInstance(project).shortenClassReferences(newAnnotation) as PsiAnnotation + JavaCodeStyleManager.getInstance(project).optimizeImports(modifierList.containingFile) val annotationIndex = modifierList.annotations.indexOf(newAnnotation) val formattedModifierList = - CodeStyleManager.getInstance(context.project).reformat(modifierList) as PsiModifierList + CodeStyleManager.getInstance(project).reformat(modifierList) as PsiModifierList return formattedModifierList.annotations.getOrNull(annotationIndex) } @@ -1271,13 +1274,25 @@ object MEExpressionCompletionUtil { val fixedNewArrayExpr = factory.createExpression("new ?[?]") as MENewExpression fixedNewArrayExpr.type!!.replace(type) variants += fixedNewArrayExpr + + val arrayLitExpr = factory.createExpression("new ?[]{?}") as MENewExpression + arrayLitExpr.type!!.replace(type) + variants += arrayLitExpr } } + is MESuperCallExpression -> { + // Might be missing its parentheses + val callExpr = factory.createExpression("super.?()") as MESuperCallExpression + expression.memberName?.let { callExpr.memberName!!.replace(it) } + variants += callExpr + } } return variants } + private fun createUniqueLookup(text: String) = LookupElementBuilder.create(Any(), text) + private fun LookupElement.createEliminable(uniquenessKey: String, priority: Int = 0) = EliminableLookup(uniquenessKey, this, priority) diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionMatchUtil.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionMatchUtil.kt index 1461facc4..541cdd455 100644 --- a/src/main/kotlin/platform/mixin/expression/MEExpressionMatchUtil.kt +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionMatchUtil.kt @@ -48,7 +48,7 @@ import com.llamalad7.mixinextras.expression.impl.flow.FlowValue import com.llamalad7.mixinextras.expression.impl.flow.expansion.InsnExpander import com.llamalad7.mixinextras.expression.impl.point.ExpressionContext import com.llamalad7.mixinextras.expression.impl.pool.IdentifierPool -import com.llamalad7.mixinextras.expression.impl.pool.MemberDefinition +import com.llamalad7.mixinextras.expression.impl.pool.SimpleMemberDefinition import org.objectweb.asm.Handle import org.objectweb.asm.Opcodes import org.objectweb.asm.Type @@ -171,9 +171,12 @@ object MEExpressionMatchUtil { val fields = annotation.findDeclaredAttributeValue("field")?.computeStringArray() ?: emptyList() for (field in fields) { val fieldRef = MemberReference.parse(field) ?: continue - pool.addMember(definitionId) { - it is FieldInsnNode && fieldRef.matchField(it.owner, it.name, it.desc) - } + pool.addMember( + definitionId, + SimpleMemberDefinition { + it is FieldInsnNode && fieldRef.matchField(it.owner, it.name, it.desc) + } + ) } val methods = annotation.findDeclaredAttributeValue("method")?.computeStringArray() ?: emptyList() @@ -181,7 +184,7 @@ object MEExpressionMatchUtil { val methodRef = MemberReference.parse(method) ?: continue pool.addMember( definitionId, - object : MemberDefinition { + object : SimpleMemberDefinition { override fun matches(insn: AbstractInsnNode) = insn is MethodInsnNode && methodRef.matchMethod(insn.owner, insn.name, insn.desc) @@ -202,20 +205,22 @@ object MEExpressionMatchUtil { for (localAnnotation in locals) { val localType = localAnnotation.findDeclaredAttributeValue("type")?.resolveType() val localInfo = LocalInfo.fromAnnotation(localType, localAnnotation) - pool.addMember(definitionId) { insn -> - if (insn !is VarInsnNode) { + pool.addMember(definitionId) { node -> + val virtualInsn = node.insn + if (virtualInsn !is VarInsnNode) { return@addMember false } - val actualInsn = if (insn.opcode >= Opcodes.ISTORE && insn.opcode <= Opcodes.ASTORE) { - insn.next ?: return@addMember false + val physicalInsn = InsnExpander.getRepresentative(node) + val actualInsn = if (virtualInsn.opcode >= Opcodes.ISTORE && virtualInsn.opcode <= Opcodes.ASTORE) { + physicalInsn.next ?: return@addMember false } else { - insn + physicalInsn } val unfilteredLocals = localInfo.getLocals(module, targetClass, targetMethod, actualInsn) ?: return@addMember false val filteredLocals = localInfo.matchLocals(unfilteredLocals, CollectVisitor.Mode.MATCH_ALL) - filteredLocals.any { it.index == insn.`var` } + filteredLocals.any { it.index == virtualInsn.`var` } } } } diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionTypedHandlerDelegate.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionTypedHandlerDelegate.kt new file mode 100644 index 000000000..6d714b43b --- /dev/null +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionTypedHandlerDelegate.kt @@ -0,0 +1,42 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.platform.mixin.expression + +import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEExpressionTypes +import com.intellij.codeInsight.AutoPopupController +import com.intellij.codeInsight.editorActions.TypedHandlerDelegate +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile +import com.intellij.psi.util.elementType + +class MEExpressionTypedHandlerDelegate : TypedHandlerDelegate() { + override fun checkAutoPopup(charTyped: Char, project: Project, editor: Editor, file: PsiFile): Result { + if (charTyped == ':' && file.language == MEExpressionLanguage) { + AutoPopupController.getInstance(project).autoPopupMemberLookup(editor) { + val offset = editor.caretModel.offset + it.findElementAt(offset - 1).elementType == MEExpressionTypes.TOKEN_METHOD_REF + } + return Result.STOP + } + return Result.CONTINUE + } +} diff --git a/src/main/kotlin/platform/mixin/reference/target/DefinitionReferences.kt b/src/main/kotlin/platform/mixin/reference/target/DefinitionReferences.kt index 3c61547b5..a6927b8a8 100644 --- a/src/main/kotlin/platform/mixin/reference/target/DefinitionReferences.kt +++ b/src/main/kotlin/platform/mixin/reference/target/DefinitionReferences.kt @@ -20,6 +20,7 @@ package com.demonwav.mcdev.platform.mixin.reference.target +import com.demonwav.mcdev.platform.mixin.expression.MEExpressionMatchUtil import com.demonwav.mcdev.platform.mixin.handlers.MixinAnnotationHandler import com.demonwav.mcdev.platform.mixin.reference.MixinReference import com.demonwav.mcdev.platform.mixin.util.MethodTargetMember @@ -46,12 +47,14 @@ import com.intellij.psi.PsiMember import com.intellij.psi.ResolveResult import com.intellij.psi.search.GlobalSearchScope import com.intellij.util.containers.sequenceOfNotNull -import org.objectweb.asm.tree.AbstractInsnNode +import com.llamalad7.mixinextras.expression.impl.flow.FlowValue +import com.llamalad7.mixinextras.expression.impl.flow.postprocessing.LMFInfo +import com.llamalad7.mixinextras.expression.impl.utils.FlowDecorations import org.objectweb.asm.tree.FieldInsnNode import org.objectweb.asm.tree.MethodInsnNode abstract class AbstractDefinitionReference : PolyReferenceResolver(), MixinReference { - abstract fun getFullReferenceIfMatches(memberReference: MemberReference, insn: AbstractInsnNode): MemberReference? + abstract fun getFullReferenceIfMatches(memberReference: MemberReference, node: FlowValue): MemberReference? abstract fun getMatchesInClass(memberReference: MemberReference, clazz: PsiClass): Sequence abstract fun referenceToString(memberReference: MemberReference): String @@ -110,8 +113,14 @@ abstract class AbstractDefinitionReference : PolyReferenceResolver(), MixinRefer continue } - for (insn in target.classAndMethod.method.instructions) { - val fullReference = getFullReferenceIfMatches(memberReference, insn) ?: continue + val flow = MEExpressionMatchUtil.getFlowMap( + project, + target.classAndMethod.clazz, + target.classAndMethod.method + ) ?: continue + + for (node in flow.values) { + val fullReference = getFullReferenceIfMatches(memberReference, node) ?: continue result += fullReference } } @@ -124,7 +133,8 @@ object FieldDefinitionReference : AbstractDefinitionReference() { val ELEMENT_PATTERN = PsiJavaPatterns.psiLiteral(StandardPatterns.string()) .insideAnnotationAttribute(MixinConstants.MixinExtras.DEFINITION, "field") - override fun getFullReferenceIfMatches(memberReference: MemberReference, insn: AbstractInsnNode): MemberReference? { + override fun getFullReferenceIfMatches(memberReference: MemberReference, node: FlowValue): MemberReference? { + val insn = node.insn if (insn !is FieldInsnNode || !memberReference.matchField(insn.owner, insn.name, insn.desc)) { return null } @@ -145,12 +155,21 @@ object MethodDefinitionReference : AbstractDefinitionReference() { val ELEMENT_PATTERN = PsiJavaPatterns.psiLiteral(StandardPatterns.string()) .insideAnnotationAttribute(MixinConstants.MixinExtras.DEFINITION, "method") - override fun getFullReferenceIfMatches(memberReference: MemberReference, insn: AbstractInsnNode): MemberReference? { - if (insn !is MethodInsnNode || !memberReference.matchMethod(insn.owner, insn.name, insn.desc)) { + override fun getFullReferenceIfMatches(memberReference: MemberReference, node: FlowValue): MemberReference? { + val info = node.getDecoration(FlowDecorations.LMF_INFO) + val insn = node.insn + val (owner, name, desc) = when { + info != null && (info.type == LMFInfo.Type.FREE_METHOD || info.type == LMFInfo.Type.BOUND_METHOD) -> + Triple(info.impl.owner, info.impl.name, info.impl.desc) + + insn is MethodInsnNode -> Triple(insn.owner, insn.name, insn.desc) + else -> return null + } + if (!memberReference.matchMethod(owner, name, desc)) { return null } - return MemberReference(insn.name, insn.desc, insn.owner.replace('/', '.')) + return MemberReference(name, desc, owner.replace('/', '.')) } override fun getMatchesInClass(memberReference: MemberReference, clazz: PsiClass) = diff --git a/src/main/kotlin/platform/mixin/util/MixinConstants.kt b/src/main/kotlin/platform/mixin/util/MixinConstants.kt index 67f145214..173fc2050 100644 --- a/src/main/kotlin/platform/mixin/util/MixinConstants.kt +++ b/src/main/kotlin/platform/mixin/util/MixinConstants.kt @@ -84,6 +84,7 @@ object MixinConstants { } object MixinExtras { + const val PACKAGE = "com.llamalad7.mixinextras." const val OPERATION = "com.llamalad7.mixinextras.injector.wrapoperation.Operation" const val WRAP_OPERATION = "com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation" const val WRAP_METHOD = "com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod" diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 307909a6e..273df3975 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -514,6 +514,7 @@ +