User: joe Date: 24 Nov 23 15:30 Revision: 8ac07f93c4d335b330ff66762e41f367c351a6d6 Summary: Rename file from AbstractInvokeInjectionPoint to InvokeInjectionPoint TeamCity URL: https://ci.mcdev.io/viewModification.html?tab=vcsModificationFiles&modId=8857&personal=false Index: src/main/kotlin/platform/mixin/handlers/injectionPoint/AbstractInvokeInjectionPoint.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/injectionPoint/AbstractInvokeInjectionPoint.kt (revision 151f2eac15cbbd813ac61f3aa786a8b984f7bdda) +++ src/main/kotlin/platform/mixin/handlers/injectionPoint/AbstractInvokeInjectionPoint.kt (revision 151f2eac15cbbd813ac61f3aa786a8b984f7bdda) @@ -1,169 +0,0 @@ -/* - * Minecraft Development for IntelliJ - * - * https://mcdev.io/ - * - * Copyright (C) 2023 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.handlers.injectionPoint - -import com.demonwav.mcdev.platform.mixin.reference.MixinSelector -import com.demonwav.mcdev.util.MemberReference -import com.intellij.openapi.project.Project -import com.intellij.psi.CommonClassNames -import com.intellij.psi.JavaPsiFacade -import com.intellij.psi.PsiAnnotation -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiClassType -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiForeachStatement -import com.intellij.psi.PsiMethod -import com.intellij.psi.PsiMethodCallExpression -import com.intellij.psi.PsiNewExpression -import org.objectweb.asm.tree.ClassNode -import org.objectweb.asm.tree.MethodInsnNode -import org.objectweb.asm.tree.MethodNode - -abstract class AbstractInvokeInjectionPoint(private val assign: Boolean) : AbstractMethodInjectionPoint() { - override fun createNavigationVisitor( - at: PsiAnnotation, - target: MixinSelector?, - targetClass: PsiClass, - ): NavigationVisitor? { - return target?.let { MyNavigationVisitor(targetClass, it) } - } - - override fun doCreateCollectVisitor( - at: PsiAnnotation, - target: MixinSelector?, - targetClass: ClassNode, - mode: CollectVisitor.Mode, - ): CollectVisitor? { - if (mode == CollectVisitor.Mode.COMPLETION) { - return MyCollectVisitor(mode, at.project, MemberReference(""), assign) - } - return target?.let { MyCollectVisitor(mode, at.project, it, assign) } - } - - private class MyNavigationVisitor( - private val targetClass: PsiClass, - private val selector: MixinSelector, - ) : NavigationVisitor() { - - private fun visitMethodUsage(method: PsiMethod, qualifier: PsiClass?, expression: PsiElement) { - if (selector.matchMethod(method, qualifier ?: targetClass)) { - addResult(expression) - } - } - - override fun visitMethodCallExpression(expression: PsiMethodCallExpression) { - val method = expression.resolveMethod() - if (method != null) { - val containingClass = method.containingClass - - // Normally, Java uses the type of the instance to qualify the method calls - // However, if the method is part of java.lang.Object (e.g. equals or toString) - // and no class in the hierarchy of the instance overrides the method, Java will - // insert the call using java.lang.Object as the owner - val qualifier = - if (method.isConstructor || containingClass?.qualifiedName == CommonClassNames.JAVA_LANG_OBJECT) { - containingClass - } else { - QualifiedMember.resolveQualifier(expression.methodExpression) - } - - visitMethodUsage(method, qualifier, expression) - } - - super.visitMethodCallExpression(expression) - } - - override fun visitNewExpression(expression: PsiNewExpression) { - val constructor = expression.resolveConstructor() - if (constructor != null) { - visitMethodUsage(constructor, constructor.containingClass!!, expression) - } - - super.visitNewExpression(expression) - } - - override fun visitForeachStatement(statement: PsiForeachStatement) { - // Enhanced for loops get compiled to a loop calling next on an Iterator - // Since these method calls are not available in the method source we need - // to generate 3 virtual method calls: the call to get the Iterator - // (Iterable.iterator()) and "Iterator.next()" and "Iterator.hasNext()" - - val type = (statement.iteratedValue?.type as? PsiClassType)?.resolve() - if (type != null) { - // Find iterator() method - val method = type.findMethodsByName("iterator", true).first { it.parameterList.parametersCount == 0 } - if (method != null) { - visitMethodUsage(method, type, statement) - } - } - - // Get Iterator class to resolve next and hasNext - val iteratorClass = JavaPsiFacade.getInstance(statement.project) - .findClass(CommonClassNames.JAVA_UTIL_ITERATOR, statement.resolveScope) - - if (iteratorClass != null) { - val hasNext = - iteratorClass.findMethodsByName("hasNext", false).first { it.parameterList.parametersCount == 0 } - if (hasNext != null) { - visitMethodUsage(hasNext, iteratorClass, statement) - } - - val next = - iteratorClass.findMethodsByName("next", false).first { it.parameterList.parametersCount == 0 } - if (next != null) { - visitMethodUsage(next, iteratorClass, statement) - } - } - - super.visitForeachStatement(statement) - } - } - - private class MyCollectVisitor( - mode: Mode, - private val project: Project, - private val selector: MixinSelector, - private val assign: Boolean, - ) : CollectVisitor(mode) { - override fun accept(methodNode: MethodNode) { - val insns = methodNode.instructions ?: return - insns.iterator().forEachRemaining { insn -> - if (insn !is MethodInsnNode) { - return@forEachRemaining - } - - val sourceMethod = nodeMatchesSelector(insn, mode, selector, project) ?: return@forEachRemaining - val actualInsn = if (assign) insn.next else insn - if (actualInsn != null) { - addResult( - actualInsn, - sourceMethod, - qualifier = insn.owner.replace('/', '.'), - ) - } - } - } - } -} - -class InvokeInjectionPoint : AbstractInvokeInjectionPoint(false) - -class InvokeAssignInjectionPoint : AbstractInvokeInjectionPoint(true) Index: src/main/kotlin/platform/mixin/handlers/injectionPoint/InvokeInjectionPoint.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/injectionPoint/InvokeInjectionPoint.kt (revision 8ac07f93c4d335b330ff66762e41f367c351a6d6) +++ src/main/kotlin/platform/mixin/handlers/injectionPoint/InvokeInjectionPoint.kt (revision 8ac07f93c4d335b330ff66762e41f367c351a6d6) @@ -0,0 +1,169 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2023 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.handlers.injectionPoint + +import com.demonwav.mcdev.platform.mixin.reference.MixinSelector +import com.demonwav.mcdev.util.MemberReference +import com.intellij.openapi.project.Project +import com.intellij.psi.CommonClassNames +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiForeachStatement +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiMethodCallExpression +import com.intellij.psi.PsiNewExpression +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.tree.MethodInsnNode +import org.objectweb.asm.tree.MethodNode + +abstract class AbstractInvokeInjectionPoint(private val assign: Boolean) : AbstractMethodInjectionPoint() { + override fun createNavigationVisitor( + at: PsiAnnotation, + target: MixinSelector?, + targetClass: PsiClass, + ): NavigationVisitor? { + return target?.let { MyNavigationVisitor(targetClass, it) } + } + + override fun doCreateCollectVisitor( + at: PsiAnnotation, + target: MixinSelector?, + targetClass: ClassNode, + mode: CollectVisitor.Mode, + ): CollectVisitor? { + if (mode == CollectVisitor.Mode.COMPLETION) { + return MyCollectVisitor(mode, at.project, MemberReference(""), assign) + } + return target?.let { MyCollectVisitor(mode, at.project, it, assign) } + } + + private class MyNavigationVisitor( + private val targetClass: PsiClass, + private val selector: MixinSelector, + ) : NavigationVisitor() { + + private fun visitMethodUsage(method: PsiMethod, qualifier: PsiClass?, expression: PsiElement) { + if (selector.matchMethod(method, qualifier ?: targetClass)) { + addResult(expression) + } + } + + override fun visitMethodCallExpression(expression: PsiMethodCallExpression) { + val method = expression.resolveMethod() + if (method != null) { + val containingClass = method.containingClass + + // Normally, Java uses the type of the instance to qualify the method calls + // However, if the method is part of java.lang.Object (e.g. equals or toString) + // and no class in the hierarchy of the instance overrides the method, Java will + // insert the call using java.lang.Object as the owner + val qualifier = + if (method.isConstructor || containingClass?.qualifiedName == CommonClassNames.JAVA_LANG_OBJECT) { + containingClass + } else { + QualifiedMember.resolveQualifier(expression.methodExpression) + } + + visitMethodUsage(method, qualifier, expression) + } + + super.visitMethodCallExpression(expression) + } + + override fun visitNewExpression(expression: PsiNewExpression) { + val constructor = expression.resolveConstructor() + if (constructor != null) { + visitMethodUsage(constructor, constructor.containingClass!!, expression) + } + + super.visitNewExpression(expression) + } + + override fun visitForeachStatement(statement: PsiForeachStatement) { + // Enhanced for loops get compiled to a loop calling next on an Iterator + // Since these method calls are not available in the method source we need + // to generate 3 virtual method calls: the call to get the Iterator + // (Iterable.iterator()) and "Iterator.next()" and "Iterator.hasNext()" + + val type = (statement.iteratedValue?.type as? PsiClassType)?.resolve() + if (type != null) { + // Find iterator() method + val method = type.findMethodsByName("iterator", true).first { it.parameterList.parametersCount == 0 } + if (method != null) { + visitMethodUsage(method, type, statement) + } + } + + // Get Iterator class to resolve next and hasNext + val iteratorClass = JavaPsiFacade.getInstance(statement.project) + .findClass(CommonClassNames.JAVA_UTIL_ITERATOR, statement.resolveScope) + + if (iteratorClass != null) { + val hasNext = + iteratorClass.findMethodsByName("hasNext", false).first { it.parameterList.parametersCount == 0 } + if (hasNext != null) { + visitMethodUsage(hasNext, iteratorClass, statement) + } + + val next = + iteratorClass.findMethodsByName("next", false).first { it.parameterList.parametersCount == 0 } + if (next != null) { + visitMethodUsage(next, iteratorClass, statement) + } + } + + super.visitForeachStatement(statement) + } + } + + private class MyCollectVisitor( + mode: Mode, + private val project: Project, + private val selector: MixinSelector, + private val assign: Boolean, + ) : CollectVisitor(mode) { + override fun accept(methodNode: MethodNode) { + val insns = methodNode.instructions ?: return + insns.iterator().forEachRemaining { insn -> + if (insn !is MethodInsnNode) { + return@forEachRemaining + } + + val sourceMethod = nodeMatchesSelector(insn, mode, selector, project) ?: return@forEachRemaining + val actualInsn = if (assign) insn.next else insn + if (actualInsn != null) { + addResult( + actualInsn, + sourceMethod, + qualifier = insn.owner.replace('/', '.'), + ) + } + } + } + } +} + +class InvokeInjectionPoint : AbstractInvokeInjectionPoint(false) + +class InvokeAssignInjectionPoint : AbstractInvokeInjectionPoint(true)