From 2b6720e263c240c425a7891b50946f258406bd1f Mon Sep 17 00:00:00 2001 From: ShortyDev Date: Tue, 16 Dec 2025 05:02:00 +0100 Subject: [PATCH 1/2] Implement member reference linking and renaming for InvokeDynamicConstant --- .../constant/InvokeDynamicConstant.java | 12 +++ .../editor/MemberReferenceFixer.java | 24 +++++ .../util/LambdaReferenceInitializer.java | 95 +++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 base/src/main/java/proguard/classfile/util/LambdaReferenceInitializer.java diff --git a/base/src/main/java/proguard/classfile/constant/InvokeDynamicConstant.java b/base/src/main/java/proguard/classfile/constant/InvokeDynamicConstant.java index d70961b17..9c2690235 100644 --- a/base/src/main/java/proguard/classfile/constant/InvokeDynamicConstant.java +++ b/base/src/main/java/proguard/classfile/constant/InvokeDynamicConstant.java @@ -38,6 +38,18 @@ public class InvokeDynamicConstant extends Constant { */ public Clazz[] referencedClasses; + /** + * An extra field pointing to the Clazz that contains the method referenced. This field is + * populated by the {@link proguard.classfile.util.LambdaReferenceInitializer}. + */ + public Clazz referencedClass; + + /** + * An extra field pointing to the Member referenced by this InvokeDynamic. This field is + * populated by the {@link proguard.classfile.util.LambdaReferenceInitializer}. + */ + public Member referencedMember; + /** Creates an uninitialized InvokeDynamicConstant. */ public InvokeDynamicConstant() {} diff --git a/base/src/main/java/proguard/classfile/editor/MemberReferenceFixer.java b/base/src/main/java/proguard/classfile/editor/MemberReferenceFixer.java index ed6f7fcff..744f78f95 100644 --- a/base/src/main/java/proguard/classfile/editor/MemberReferenceFixer.java +++ b/base/src/main/java/proguard/classfile/editor/MemberReferenceFixer.java @@ -199,6 +199,30 @@ public void visitInterfaceMethodrefConstant( } } + @Override + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + // Do we know the referenced member? + if (invokeDynamicConstant.referencedMember != null) + { + Clazz referencedClass = invokeDynamicConstant.referencedClass; + Member referencedMember = invokeDynamicConstant.referencedMember; + + // Does it have a new name? + String newName = referencedMember.getName(referencedClass); + String currentName = invokeDynamicConstant.getName(clazz); + + if (!currentName.equals(newName)) + { + String currentDescriptor = invokeDynamicConstant.getType(clazz); + + // Update the name and type index. + invokeDynamicConstant.u2nameAndTypeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, currentDescriptor); + } + } + } + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) { // Do we know the referenced method? Method referencedMethod = methodrefConstant.referencedMethod; diff --git a/base/src/main/java/proguard/classfile/util/LambdaReferenceInitializer.java b/base/src/main/java/proguard/classfile/util/LambdaReferenceInitializer.java new file mode 100644 index 000000000..ff81cef92 --- /dev/null +++ b/base/src/main/java/proguard/classfile/util/LambdaReferenceInitializer.java @@ -0,0 +1,95 @@ +/* + * ProGuardCORE -- library to process Java bytecode. + * + * Copyright (c) 2002-2025 Guardsquare NV + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package proguard.classfile.util; + +import proguard.classfile.ClassPool; +import proguard.classfile.Clazz; +import proguard.classfile.Member; +import proguard.classfile.ProgramClass; +import proguard.classfile.constant.Constant; +import proguard.classfile.constant.InvokeDynamicConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This {@link ClassVisitor} initializes the references of {@link InvokeDynamicConstant}s + * that represent lambda expressions. More specifically, it links the constants to the + * actual functional interface methods they target in the program class pool or in the + * library class pool. + * + *

The class hierarchy must be initialized before using this visitor. + * + * @author ShortyDev + */ +public class LambdaReferenceInitializer + implements ClassVisitor, + ConstantVisitor { + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + + public LambdaReferenceInitializer(ClassPool programClassPool, ClassPool libraryClassPool) { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + } + + // Implementations for ClassVisitor. + @Override + public void visitAnyClass(Clazz clazz) { + } + + @Override + public void visitProgramClass(ProgramClass programClass) { + programClass.constantPoolEntriesAccept(this); + } + + // Implementations for ConstantVisitor. + @Override + public void visitAnyConstant(Clazz clazz, Constant constant) { + } + + @Override + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) { + // Get the descriptor to find the return type. + String descriptor = invokeDynamicConstant.getType(clazz); + String returnType = ClassUtil.internalMethodReturnType(descriptor); + + // Check if the return type is a class type. + if (ClassUtil.isInternalClassType(returnType)) { + String interfaceClassName = ClassUtil.internalClassNameFromClassType(returnType); + + // Find the interface class. + Clazz interfaceClass = programClassPool.getClass(interfaceClassName); + + if (interfaceClass == null) { + interfaceClass = libraryClassPool.getClass(interfaceClassName); + } + + if (interfaceClass != null) { + String methodName = invokeDynamicConstant.getName(clazz); + + // Find the method in the interface. + Member referencedMember = interfaceClass.findMethod(methodName, null); + + if (referencedMember != null) { + invokeDynamicConstant.referencedClass = interfaceClass; + invokeDynamicConstant.referencedMember = referencedMember; + } + } + } + } +} \ No newline at end of file From 36cc5586de5bd83b3d0e8726535fd876c9fe6daf Mon Sep 17 00:00:00 2001 From: ShortyDev Date: Tue, 16 Dec 2025 11:44:19 +0100 Subject: [PATCH 2/2] Apply proper formatting --- .../constant/InvokeDynamicConstant.java | 4 ++-- .../editor/MemberReferenceFixer.java | 16 +++++++--------- .../util/LambdaReferenceInitializer.java | 19 +++++++------------ 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/base/src/main/java/proguard/classfile/constant/InvokeDynamicConstant.java b/base/src/main/java/proguard/classfile/constant/InvokeDynamicConstant.java index 9c2690235..2eb1e88f7 100644 --- a/base/src/main/java/proguard/classfile/constant/InvokeDynamicConstant.java +++ b/base/src/main/java/proguard/classfile/constant/InvokeDynamicConstant.java @@ -45,8 +45,8 @@ public class InvokeDynamicConstant extends Constant { public Clazz referencedClass; /** - * An extra field pointing to the Member referenced by this InvokeDynamic. This field is - * populated by the {@link proguard.classfile.util.LambdaReferenceInitializer}. + * An extra field pointing to the Member referenced by this InvokeDynamic. This field is populated + * by the {@link proguard.classfile.util.LambdaReferenceInitializer}. */ public Member referencedMember; diff --git a/base/src/main/java/proguard/classfile/editor/MemberReferenceFixer.java b/base/src/main/java/proguard/classfile/editor/MemberReferenceFixer.java index 744f78f95..b3d6d5a18 100644 --- a/base/src/main/java/proguard/classfile/editor/MemberReferenceFixer.java +++ b/base/src/main/java/proguard/classfile/editor/MemberReferenceFixer.java @@ -200,25 +200,23 @@ public void visitInterfaceMethodrefConstant( } @Override - public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) - { + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) { // Do we know the referenced member? - if (invokeDynamicConstant.referencedMember != null) - { - Clazz referencedClass = invokeDynamicConstant.referencedClass; + if (invokeDynamicConstant.referencedMember != null) { + Clazz referencedClass = invokeDynamicConstant.referencedClass; Member referencedMember = invokeDynamicConstant.referencedMember; // Does it have a new name? - String newName = referencedMember.getName(referencedClass); + String newName = referencedMember.getName(referencedClass); String currentName = invokeDynamicConstant.getName(clazz); - if (!currentName.equals(newName)) - { + if (!currentName.equals(newName)) { String currentDescriptor = invokeDynamicConstant.getType(clazz); // Update the name and type index. invokeDynamicConstant.u2nameAndTypeIndex = - new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, currentDescriptor); + new ConstantPoolEditor((ProgramClass) clazz) + .addNameAndTypeConstant(newName, currentDescriptor); } } } diff --git a/base/src/main/java/proguard/classfile/util/LambdaReferenceInitializer.java b/base/src/main/java/proguard/classfile/util/LambdaReferenceInitializer.java index ff81cef92..2dbae41a3 100644 --- a/base/src/main/java/proguard/classfile/util/LambdaReferenceInitializer.java +++ b/base/src/main/java/proguard/classfile/util/LambdaReferenceInitializer.java @@ -27,18 +27,15 @@ import proguard.classfile.visitor.ClassVisitor; /** - * This {@link ClassVisitor} initializes the references of {@link InvokeDynamicConstant}s - * that represent lambda expressions. More specifically, it links the constants to the - * actual functional interface methods they target in the program class pool or in the - * library class pool. + * This {@link ClassVisitor} initializes the references of {@link InvokeDynamicConstant}s that + * represent lambda expressions. More specifically, it links the constants to the actual functional + * interface methods they target in the program class pool or in the library class pool. * *

The class hierarchy must be initialized before using this visitor. * * @author ShortyDev */ -public class LambdaReferenceInitializer - implements ClassVisitor, - ConstantVisitor { +public class LambdaReferenceInitializer implements ClassVisitor, ConstantVisitor { private final ClassPool programClassPool; private final ClassPool libraryClassPool; @@ -49,8 +46,7 @@ public LambdaReferenceInitializer(ClassPool programClassPool, ClassPool libraryC // Implementations for ClassVisitor. @Override - public void visitAnyClass(Clazz clazz) { - } + public void visitAnyClass(Clazz clazz) {} @Override public void visitProgramClass(ProgramClass programClass) { @@ -59,8 +55,7 @@ public void visitProgramClass(ProgramClass programClass) { // Implementations for ConstantVisitor. @Override - public void visitAnyConstant(Clazz clazz, Constant constant) { - } + public void visitAnyConstant(Clazz clazz, Constant constant) {} @Override public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) { @@ -92,4 +87,4 @@ public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invoke } } } -} \ No newline at end of file +}