User: joe Date: 09 May 25 23:00 Revision: 253930a614114109306e7efe4b7c71343b7a1243 Summary: Desugar varargs TeamCity URL: https://ci.mcdev.io/viewModification.html?tab=vcsModificationFiles&modId=9989&personal=false Index: src/main/kotlin/platform/mixin/handlers/desugar/DesugarUtil.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/desugar/DesugarUtil.kt (revision 0b58959f04b911a69833c824248e5161c3b51d14) +++ src/main/kotlin/platform/mixin/handlers/desugar/DesugarUtil.kt (revision 253930a614114109306e7efe4b7c71343b7a1243) @@ -26,14 +26,15 @@ import com.intellij.psi.JavaRecursiveElementWalkingVisitor import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile +import com.intellij.psi.PsiJavaFile import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.parents object DesugarUtil { private val ORIGINAL_ELEMENT_KEY = Key.create("mcdev.desugar.originalElement") - private val DESUGARERS = arrayOf( + private val DESUGARERS = arrayOf( + RemoveVarArgsDesugarer, FieldAssignmentDesugarer, ) @@ -45,13 +46,13 @@ desugared.putCopyableUserData(ORIGINAL_ELEMENT_KEY, original) } - fun desugar(project: Project, clazz: PsiClass): PsiClass { + fun desugar(project: Project, clazz: PsiClass): PsiClass? { return clazz.cached { - val desugaredFile = clazz.containingFile.copy() as PsiFile - val desugaredClass = PsiTreeUtil.findSameElementInCopy(clazz, desugaredFile) + val desugaredFile = clazz.containingFile.copy() as? PsiJavaFile ?: return@cached null + var desugaredClass = PsiTreeUtil.findSameElementInCopy(clazz, desugaredFile) setOriginalRecursive(desugaredClass, clazz) for (desugarer in DESUGARERS) { - desugarer.desugar(project, desugaredClass) + desugaredClass = desugarer.desugar(project, desugaredFile, desugaredClass) } desugaredClass } Index: src/main/kotlin/platform/mixin/handlers/desugar/Desugarer.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/desugar/Desugarer.kt (revision 0b58959f04b911a69833c824248e5161c3b51d14) +++ src/main/kotlin/platform/mixin/handlers/desugar/Desugarer.kt (revision 253930a614114109306e7efe4b7c71343b7a1243) @@ -22,7 +22,15 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiClass +import com.intellij.psi.PsiJavaFile +import com.intellij.psi.util.PsiTreeUtil -interface Desugarer { - fun desugar(project: Project, clazz: PsiClass) +abstract class Desugarer { + abstract fun desugar(project: Project, file: PsiJavaFile, clazz: PsiClass): PsiClass + + companion object { + @JvmStatic + protected val PsiJavaFile.allClasses: Collection + get() = PsiTreeUtil.findChildrenOfType(this, PsiClass::class.java) -} + } +} Index: src/main/kotlin/platform/mixin/handlers/desugar/FieldAssignmentDesugarer.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/desugar/FieldAssignmentDesugarer.kt (revision 0b58959f04b911a69833c824248e5161c3b51d14) +++ src/main/kotlin/platform/mixin/handlers/desugar/FieldAssignmentDesugarer.kt (revision 253930a614114109306e7efe4b7c71343b7a1243) @@ -28,84 +28,90 @@ import com.intellij.psi.PsiClassInitializer import com.intellij.psi.PsiExpressionStatement import com.intellij.psi.PsiField +import com.intellij.psi.PsiJavaFile import com.intellij.psi.PsiMethod import com.intellij.psi.PsiModifier import com.intellij.psi.PsiStatement import com.intellij.psi.PsiType import com.intellij.util.JavaPsiConstructorUtil -object FieldAssignmentDesugarer : Desugarer { - override fun desugar(project: Project, clazz: PsiClass) { +object FieldAssignmentDesugarer : Desugarer() { + override fun desugar(project: Project, file: PsiJavaFile, clazz: PsiClass): PsiClass { val staticStatementsToInsertPre = mutableListOf() val staticStatementsToInsertPost = mutableListOf() val nonStaticStatementsToInsert = mutableListOf() var seenStaticInitializer = false - for (child in clazz.children) { + for (aClass in file.allClasses) { + for (child in aClass.children) { - when (child) { - is PsiField -> { - val initializer = child.initializer ?: continue + when (child) { + is PsiField -> { + val initializer = child.initializer ?: continue - if (child.hasModifierProperty(PsiModifier.STATIC)) { - // check if the field is a ConstantValue with no initializer in the bytecode - val constantValue = initializer.constantValue - if (constantValue != null && constantValue !is PsiType) { - continue - } + if (child.hasModifierProperty(PsiModifier.STATIC)) { + // check if the field is a ConstantValue with no initializer in the bytecode + val constantValue = initializer.constantValue + if (constantValue != null && constantValue !is PsiType) { + continue + } - val fieldInitializer = JavaPsiFacade.getElementFactory(project) - .createStatementFromText("${child.name} = null;", child) as PsiExpressionStatement - (fieldInitializer.expression as PsiAssignmentExpression).rExpression!!.replace(initializer) - DesugarUtil.setOriginalElement(fieldInitializer, DesugarUtil.getOriginalElement(child)) + val fieldInitializer = JavaPsiFacade.getElementFactory(project) + .createStatementFromText("${child.name} = null;", child) as PsiExpressionStatement + (fieldInitializer.expression as PsiAssignmentExpression).rExpression!!.replace(initializer) + DesugarUtil.setOriginalElement(fieldInitializer, DesugarUtil.getOriginalElement(child)) - if (seenStaticInitializer) { - staticStatementsToInsertPost += fieldInitializer - } else { - staticStatementsToInsertPre += fieldInitializer - } - } else { - val fieldInitializer = JavaPsiFacade.getElementFactory(project) - .createStatementFromText("this.${child.name} = null;", child) as PsiExpressionStatement - (fieldInitializer.expression as PsiAssignmentExpression).rExpression!!.replace(initializer) - DesugarUtil.setOriginalElement(fieldInitializer, DesugarUtil.getOriginalElement(child)) + if (seenStaticInitializer) { + staticStatementsToInsertPost += fieldInitializer + } else { + staticStatementsToInsertPre += fieldInitializer + } + } else { + val fieldInitializer = JavaPsiFacade.getElementFactory(project) + .createStatementFromText("this.${child.name} = null;", child) as PsiExpressionStatement + (fieldInitializer.expression as PsiAssignmentExpression).rExpression!!.replace(initializer) + DesugarUtil.setOriginalElement(fieldInitializer, DesugarUtil.getOriginalElement(child)) - nonStaticStatementsToInsert += fieldInitializer - } + nonStaticStatementsToInsert += fieldInitializer + } - initializer.delete() - } + initializer.delete() + } + - is PsiClassInitializer -> { - if (child.hasModifierProperty(PsiModifier.STATIC)) { - seenStaticInitializer = true - } else { - nonStaticStatementsToInsert += child.body.statements - child.delete() - } - } - } - } + is PsiClassInitializer -> { + if (child.hasModifierProperty(PsiModifier.STATIC)) { + seenStaticInitializer = true + } else { + nonStaticStatementsToInsert += child.body.statements + child.delete() + } + } + } + } - if (staticStatementsToInsertPre.isNotEmpty() || staticStatementsToInsertPost.isNotEmpty()) { + if (staticStatementsToInsertPre.isNotEmpty() || staticStatementsToInsertPost.isNotEmpty()) { - val staticBlock = findStaticBlock(project, clazz) + val staticBlock = findStaticBlock(project, aClass) - for (statement in staticStatementsToInsertPre) { - staticBlock.body.addAfter(statement, staticBlock.body.lBrace) - } - for (statement in staticStatementsToInsertPost) { - staticBlock.body.addBefore(statement, staticBlock.body.rBrace) - } - } + for (statement in staticStatementsToInsertPre) { + staticBlock.body.addAfter(statement, staticBlock.body.lBrace) + } + for (statement in staticStatementsToInsertPost) { + staticBlock.body.addBefore(statement, staticBlock.body.rBrace) + } + } - if (nonStaticStatementsToInsert.isNotEmpty()) { + if (nonStaticStatementsToInsert.isNotEmpty()) { - for (constructor in findConstructorsCallingSuper(project, clazz)) { + for (constructor in findConstructorsCallingSuper(project, aClass)) { - val body = constructor.body ?: continue - val delegateCtorCall = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(constructor) - for (statement in nonStaticStatementsToInsert.asReversed()) { - body.addAfter(statement, delegateCtorCall?.parent ?: body.lBrace) - } - } - } - } + val body = constructor.body ?: continue + val delegateCtorCall = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(constructor) + for (statement in nonStaticStatementsToInsert.asReversed()) { + body.addAfter(statement, delegateCtorCall?.parent ?: body.lBrace) + } + } + } + } + return clazz + } + private fun findStaticBlock(project: Project, clazz: PsiClass): PsiClassInitializer { for (initializer in clazz.initializers) { if (initializer.hasModifierProperty(PsiModifier.STATIC)) { Index: src/main/kotlin/platform/mixin/handlers/desugar/RemoveVarArgsDesugarer.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/desugar/RemoveVarArgsDesugarer.kt (revision 253930a614114109306e7efe4b7c71343b7a1243) +++ src/main/kotlin/platform/mixin/handlers/desugar/RemoveVarArgsDesugarer.kt (revision 253930a614114109306e7efe4b7c71343b7a1243) @@ -0,0 +1,101 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2025 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.desugar + +import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil +import com.intellij.openapi.project.Project +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiCall +import com.intellij.psi.PsiCapturedWildcardType +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiJavaFile +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiNewExpression +import com.intellij.psi.PsiTypeCastExpression +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.PsiTypesUtil +import com.intellij.psi.util.TypeConversionUtil +import com.siyeh.ig.psiutils.MethodCallUtils + +object RemoveVarArgsDesugarer : Desugarer() { + override fun desugar(project: Project, file: PsiJavaFile, clazz: PsiClass): PsiClass { + val varArgsStarts = mutableListOf>() + PsiTreeUtil.processElements(file) { element -> + if (element is PsiCall && MethodCallUtils.isVarArgCall(element)) { + val argumentCount = element.resolveMethod()?.parameterList?.parametersCount ?: return@processElements true + varArgsStarts += element to argumentCount - 1 + } + true + } + + val elementFactory = JavaPsiFacade.getElementFactory(project) + + for ((call, varArgsStart) in varArgsStarts.asReversed()) { + val argumentList = call.argumentList ?: continue + val arguments = argumentList.expressions + + val result = call.resolveMethodGenerics() + val method = result.element as? PsiMethod ?: continue + val parameters = method.parameterList.parameters + val componentType = PsiTypesUtil.getParameterType( + parameters, + parameters.lastIndex, + true + ) + + var type = result.substitutor.substitute(componentType) + if (type is PsiCapturedWildcardType) { + type = type.lowerBound + } + + val (replacementExpr, newArrayExpr) = if (JavaGenericsUtil.isReifiableType(type)) { + val newExpr = elementFactory.createExpressionFromText("new ${type.canonicalText}[] {}", call) + as PsiNewExpression + newExpr to newExpr + } else { + val erasure = TypeConversionUtil.erasure(type) + val castExpr = elementFactory.createExpressionFromText("(${type.canonicalText}) new ${erasure.canonicalText}[] {}", call) + as PsiTypeCastExpression + castExpr to castExpr.operand as PsiNewExpression + } + + if (arguments.size > varArgsStart) { + DesugarUtil.setOriginalElement( + replacementExpr, + DesugarUtil.getOriginalElement(arguments[varArgsStart]) + ) + newArrayExpr.arrayInitializer!!.addRange(arguments[varArgsStart], arguments.last()) + for (i in varArgsStart until arguments.size) { + arguments[i].delete() + } + } else { + DesugarUtil.setOriginalElement( + replacementExpr, + argumentList.lastChild?.let(DesugarUtil::getOriginalElement) + ) + } + + argumentList.add(replacementExpr) + } + + return clazz + } +} Index: src/main/kotlin/platform/mixin/handlers/injectionPoint/AtResolver.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/injectionPoint/AtResolver.kt (revision 0b58959f04b911a69833c824248e5161c3b51d14) +++ src/main/kotlin/platform/mixin/handlers/injectionPoint/AtResolver.kt (revision 253930a614114109306e7efe4b7c71343b7a1243) @@ -236,7 +236,7 @@ val targetPsiFile = targetPsiClass.containingFile ?: return emptyList() // Desugar the target class - val desugaredTargetClass = DesugarUtil.desugar(project, targetPsiClass) + val desugaredTargetClass = DesugarUtil.desugar(project, targetPsiClass) ?: return emptyList() // Find the element in the desugared class, first by directly searching and then by searching in the original // and reverse mapping it into the desugared class.