User: joe
Date: 23 Jan 24 00:18
Revision: 53c3cca60486df0c2e0afe88f31ed99f0c825f58
Summary:
Add CTOR_HEAD injection point
TeamCity URL: https://ci.mcdev.io/viewModification.html?tab=vcsModificationFiles&modId=9029&personal=false
Index: src/main/kotlin/platform/mixin/handlers/injectionPoint/CtorHeadInjectionPoint.kt
===================================================================
--- src/main/kotlin/platform/mixin/handlers/injectionPoint/CtorHeadInjectionPoint.kt (revision 53c3cca60486df0c2e0afe88f31ed99f0c825f58)
+++ src/main/kotlin/platform/mixin/handlers/injectionPoint/CtorHeadInjectionPoint.kt (revision 53c3cca60486df0c2e0afe88f31ed99f0c825f58)
@@ -0,0 +1,194 @@
+/*
+ * 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.handlers.injectionPoint
+
+import com.demonwav.mcdev.platform.mixin.reference.MixinSelector
+import com.demonwav.mcdev.platform.mixin.util.findOrConstructSourceMethod
+import com.demonwav.mcdev.platform.mixin.util.findSuperConstructorCall
+import com.demonwav.mcdev.platform.mixin.util.isConstructor
+import com.demonwav.mcdev.util.createLiteralExpression
+import com.demonwav.mcdev.util.enumValueOfOrNull
+import com.demonwav.mcdev.util.findContainingClass
+import com.intellij.codeInsight.lookup.LookupElementBuilder
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.project.Project
+import com.intellij.psi.JavaPsiFacade
+import com.intellij.psi.PsiAnnotation
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiExpression
+import com.intellij.psi.PsiField
+import com.intellij.psi.PsiLiteral
+import com.intellij.psi.PsiMethod
+import com.intellij.psi.PsiMethodCallExpression
+import com.intellij.psi.PsiMethodReferenceExpression
+import com.intellij.psi.PsiReferenceExpression
+import com.intellij.psi.PsiStatement
+import com.intellij.psi.codeStyle.CodeStyleManager
+import com.intellij.psi.util.PsiUtil
+import com.intellij.psi.util.parentOfType
+import com.intellij.util.JavaPsiConstructorUtil
+import org.objectweb.asm.Opcodes
+import org.objectweb.asm.tree.ClassNode
+import org.objectweb.asm.tree.FieldInsnNode
+import org.objectweb.asm.tree.MethodNode
+
+class CtorHeadInjectionPoint : InjectionPoint() {
+ override fun onCompleted(editor: Editor, reference: PsiLiteral) {
+ val at = reference.parentOfType() ?: return
+ val project = reference.project
+ at.setDeclaredAttributeValue(
+ "unsafe",
+ JavaPsiFacade.getElementFactory(project).createLiteralExpression(true)
+ )
+ CodeStyleManager.getInstance(project).reformat(at)
+ }
+
+ override fun createNavigationVisitor(
+ at: PsiAnnotation,
+ target: MixinSelector?,
+ targetClass: PsiClass
+ ): NavigationVisitor {
+ val args = AtResolver.getArgs(at)
+ val enforce = args["enforce"]?.let { enumValueOfOrNull(it) } ?: EnforceMode.DEFAULT
+ return MyNavigationVisitor(enforce)
+ }
+
+ override fun doCreateCollectVisitor(
+ at: PsiAnnotation,
+ target: MixinSelector?,
+ targetClass: ClassNode,
+ mode: CollectVisitor.Mode
+ ): CollectVisitor {
+ val args = AtResolver.getArgs(at)
+ val enforce = args["enforce"]?.let { enumValueOfOrNull(it) } ?: EnforceMode.DEFAULT
+ return MyCollectVisitor(at.project, targetClass, mode, enforce)
+ }
+
+ override fun createLookup(
+ targetClass: ClassNode,
+ result: CollectVisitor.Result
+ ): LookupElementBuilder? {
+ return null
+ }
+
+ private enum class EnforceMode {
+ DEFAULT, POST_DELEGATE, POST_INIT
+ }
+
+ private class MyCollectVisitor(
+ project: Project,
+ clazz: ClassNode,
+ mode: Mode,
+ private val enforce: EnforceMode,
+ ) : HeadInjectionPoint.MyCollectVisitor(project, clazz, mode) {
+ override fun accept(methodNode: MethodNode) {
+ val insns = methodNode.instructions ?: return
+
+ if (!methodNode.isConstructor) {
+ super.accept(methodNode)
+ return
+ }
+
+ val superCtorCall = methodNode.findSuperConstructorCall() ?: run {
+ super.accept(methodNode)
+ return
+ }
+
+ if (enforce == EnforceMode.POST_DELEGATE) {
+ val insn = superCtorCall.next ?: return
+ addResult(insn, methodNode.findOrConstructSourceMethod(clazz, project))
+ return
+ }
+
+ // Although Mumfrey's original intention was to target the last *unique* field store,
+ // i.e. ignore duplicate field stores that occur later, due to a bug in the implementation
+ // it simply finds the last PUTFIELD whose owner is the target class. Mumfrey now says he
+ // doesn't want to change the implementation in case of breaking mixins that rely on this
+ // behavior, so it is now effectively intended, so it's what we'll use here.
+ val lastFieldStore = generateSequence(insns.last) { it.previous }
+ .takeWhile { it !== superCtorCall }
+ .firstOrNull { insn ->
+ insn.opcode == Opcodes.PUTFIELD &&
+ (insn as FieldInsnNode).owner == clazz.name
+ } ?: superCtorCall
+
+ val lastFieldStoreNext = lastFieldStore.next ?: return
+ addResult(lastFieldStoreNext, methodNode.findOrConstructSourceMethod(clazz, project))
+ }
+ }
+
+ private class MyNavigationVisitor(private val enforce: EnforceMode) : NavigationVisitor() {
+ private var isConstructor = true
+ private var firstStatement = true
+ private lateinit var elementToReturn: PsiElement
+
+ override fun visitStart(executableElement: PsiElement) {
+ isConstructor = executableElement is PsiMethod && executableElement.isConstructor
+ elementToReturn = executableElement
+ }
+
+ override fun visitExpression(expression: PsiExpression) {
+ if (firstStatement) {
+ elementToReturn = expression
+ firstStatement = false
+ }
+ super.visitExpression(expression)
+ }
+
+ override fun visitStatement(statement: PsiStatement) {
+ if (firstStatement) {
+ elementToReturn = statement
+ firstStatement = false
+ }
+ super.visitStatement(statement)
+ }
+
+ override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
+ super.visitMethodCallExpression(expression)
+ if (isConstructor) {
+ if (JavaPsiConstructorUtil.isChainedConstructorCall(expression) ||
+ JavaPsiConstructorUtil.isSuperConstructorCall(expression)
+ ) {
+ elementToReturn = expression
+ }
+ }
+ }
+
+ override fun visitReferenceExpression(expression: PsiReferenceExpression) {
+ super.visitReferenceExpression(expression)
+ if (isConstructor &&
+ enforce != EnforceMode.POST_DELEGATE &&
+ expression !is PsiMethodReferenceExpression &&
+ PsiUtil.isAccessedForWriting(expression)
+ ) {
+ val resolvedField = expression.resolve()
+ if (resolvedField is PsiField && resolvedField.containingClass == expression.findContainingClass()) {
+ elementToReturn = expression
+ }
+ }
+ }
+
+ override fun visitEnd(executableElement: PsiElement) {
+ addResult(elementToReturn)
+ }
+ }
+}
Index: src/main/kotlin/platform/mixin/handlers/injectionPoint/HeadInjectionPoint.kt
===================================================================
--- src/main/kotlin/platform/mixin/handlers/injectionPoint/HeadInjectionPoint.kt (revision 855d67c62fd4f4ef7f894bd57b9e237752069426)
+++ src/main/kotlin/platform/mixin/handlers/injectionPoint/HeadInjectionPoint.kt (revision 53c3cca60486df0c2e0afe88f31ed99f0c825f58)
@@ -57,9 +57,9 @@
return null
}
- private class MyCollectVisitor(
- private val project: Project,
- private val clazz: ClassNode,
+ internal open class MyCollectVisitor(
+ protected val project: Project,
+ protected val clazz: ClassNode,
mode: Mode,
) : CollectVisitor(mode) {
override fun accept(methodNode: MethodNode) {
Index: src/main/kotlin/platform/mixin/handlers/injectionPoint/InjectionPoint.kt
===================================================================
--- src/main/kotlin/platform/mixin/handlers/injectionPoint/InjectionPoint.kt (revision 855d67c62fd4f4ef7f894bd57b9e237752069426)
+++ src/main/kotlin/platform/mixin/handlers/injectionPoint/InjectionPoint.kt (revision 53c3cca60486df0c2e0afe88f31ed99f0c825f58)
@@ -294,6 +294,9 @@
result += element
}
+ open fun visitStart(executableElement: PsiElement) {
+ }
+
open fun visitEnd(executableElement: PsiElement) {
}
@@ -304,6 +307,7 @@
override fun visitMethod(method: PsiMethod) {
if (!hasVisitedAnything) {
+ visitStart(method)
super.visitMethod(method)
visitEnd(method)
}
@@ -312,6 +316,7 @@
override fun visitAnonymousClass(aClass: PsiAnonymousClass) {
// do not recurse into anonymous classes
if (!hasVisitedAnything) {
+ visitStart(aClass)
super.visitAnonymousClass(aClass)
visitEnd(aClass)
}
@@ -320,6 +325,7 @@
override fun visitClass(aClass: PsiClass) {
// do not recurse into inner classes
if (!hasVisitedAnything) {
+ visitStart(aClass)
super.visitClass(aClass)
visitEnd(aClass)
}
@@ -327,6 +333,9 @@
override fun visitMethodReferenceExpression(expression: PsiMethodReferenceExpression) {
val hadVisitedAnything = hasVisitedAnything
+ if (!hadVisitedAnything) {
+ visitStart(expression)
+ }
super.visitMethodReferenceExpression(expression)
if (!hadVisitedAnything) {
visitEnd(expression)
@@ -336,6 +345,7 @@
override fun visitLambdaExpression(expression: PsiLambdaExpression) {
// do not recurse into lambda expressions
if (!hasVisitedAnything) {
+ visitStart(expression)
super.visitLambdaExpression(expression)
visitEnd(expression)
}
Index: src/main/kotlin/util/utils.kt
===================================================================
--- src/main/kotlin/util/utils.kt (revision 855d67c62fd4f4ef7f894bd57b9e237752069426)
+++ src/main/kotlin/util/utils.kt (revision 53c3cca60486df0c2e0afe88f31ed99f0c825f58)
@@ -388,3 +388,11 @@
return null
}
+
+inline fun > enumValueOfOrNull(str: String): T? {
+ return try {
+ enumValueOf(str)
+ } catch (e: IllegalArgumentException) {
+ null
+ }
+}
Index: src/main/resources/META-INF/plugin.xml
===================================================================
--- src/main/resources/META-INF/plugin.xml (revision 855d67c62fd4f4ef7f894bd57b9e237752069426)
+++ src/main/resources/META-INF/plugin.xml (revision 53c3cca60486df0c2e0afe88f31ed99f0c825f58)
@@ -151,6 +151,7 @@
+