From c08b6b1cd51dc76be6373cd6620ce9e21bfb5822 Mon Sep 17 00:00:00 2001 From: Pranav Date: Wed, 27 Jul 2022 19:00:11 +0530 Subject: [PATCH 1/6] Delete LambdaExpression.java --- .../proguard/backport/LambdaExpression.java | 229 ------------------ 1 file changed, 229 deletions(-) delete mode 100644 base/src/main/java/proguard/backport/LambdaExpression.java diff --git a/base/src/main/java/proguard/backport/LambdaExpression.java b/base/src/main/java/proguard/backport/LambdaExpression.java deleted file mode 100644 index babfbc040..000000000 --- a/base/src/main/java/proguard/backport/LambdaExpression.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * ProGuard -- shrinking, optimization, obfuscation, and preverification - * of Java bytecode. - * - * Copyright (c) 2002-2020 Guardsquare NV - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * 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 General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -package proguard.backport; - -import proguard.classfile.*; -import proguard.classfile.attribute.BootstrapMethodInfo; -import proguard.classfile.constant.MethodHandleConstant; -import proguard.classfile.util.*; - -/** - * A small helper class that captures useful information - * about a lambda expression as encountered in a class file. - * - * @author Thomas Neidhart - */ -public class LambdaExpression -{ - // The referenced class of the lambda expression. - public ProgramClass referencedClass; - - // The referenced bootstrap method index. - public int bootstrapMethodIndex; - // The referenced bootstrap method info. - public BootstrapMethodInfo bootstrapMethodInfo; - - // The lambda factory method type. - public String factoryMethodDescriptor; - - // The implemented interfaces of the Lambda expression. - public String[] interfaces; - - // The additional bridge method descriptors to be added. - public String[] bridgeMethodDescriptors; - - // The name and descriptor of the implemented interface method. - public String interfaceMethod; - public String interfaceMethodDescriptor; - - // Information regarding the invoked method. - public int invokedReferenceKind; - public String invokedClassName; - public String invokedMethodName; - public String invokedMethodDesc; - - public Clazz referencedInvokedClass; - public Method referencedInvokedMethod; - - // The created lambda class. - public ProgramClass lambdaClass; - - - /** - * Creates a new initialized LambdaExpression (except for the lambdaClass). - */ - public LambdaExpression(ProgramClass referencedClass, - int bootstrapMethodIndex, - BootstrapMethodInfo bootstrapMethodInfo, - String factoryMethodDescriptor, - String[] interfaces, - String[] bridgeMethodDescriptors, - String interfaceMethod, - String interfaceMethodDescriptor, - int invokedReferenceKind, - String invokedClassName, - String invokedMethodName, - String invokedMethodDesc, - Clazz referencedInvokedClass, - Method referencedInvokedMethod) - { - this.referencedClass = referencedClass; - this.bootstrapMethodIndex = bootstrapMethodIndex; - this.bootstrapMethodInfo = bootstrapMethodInfo; - this.factoryMethodDescriptor = factoryMethodDescriptor; - this.interfaces = interfaces; - this.bridgeMethodDescriptors = bridgeMethodDescriptors; - this.interfaceMethod = interfaceMethod; - this.interfaceMethodDescriptor = interfaceMethodDescriptor; - this.invokedReferenceKind = invokedReferenceKind; - this.invokedClassName = invokedClassName; - this.invokedMethodName = invokedMethodName; - this.invokedMethodDesc = invokedMethodDesc; - this.referencedInvokedClass = referencedInvokedClass; - this.referencedInvokedMethod = referencedInvokedMethod; - } - - - /** - * Returns the class name of the converted anonymous class. - */ - public String getLambdaClassName() - { - return String.format("%s$$Lambda$%d", - referencedClass.getName(), - bootstrapMethodIndex); - } - - - public String getConstructorDescriptor() - { - if (isStateless()) - { - return ClassConstants.METHOD_TYPE_INIT; - } - else - { - int endIndex = factoryMethodDescriptor.indexOf(TypeConstants.METHOD_ARGUMENTS_CLOSE); - - return factoryMethodDescriptor.substring(0, endIndex + 1) + TypeConstants.VOID; - } - } - - - /** - * Returns whether the lambda expression is serializable. - */ - public boolean isSerializable() - { - for (String interfaceName : interfaces) - { - if (ClassConstants.NAME_JAVA_IO_SERIALIZABLE.equals(interfaceName)) - { - return true; - } - } - return false; - } - - - /** - * Returns whether the lambda expression is actually a method reference. - */ - public boolean isMethodReference() - { - return !isLambdaMethod(invokedMethodName); - } - - - /** - * Returns whether the lambda expression is stateless. - */ - public boolean isStateless() - { - // The lambda expression is stateless if the factory method does - // not have arguments. - return - ClassUtil.internalMethodParameterCount(factoryMethodDescriptor) == 0; - } - - - /** - * Returns whether the invoked method is a static interface method. - */ - public boolean invokesStaticInterfaceMethod() - { - // We assume unknown classes are not interfaces. - return invokedReferenceKind == MethodHandleConstant.REF_INVOKE_STATIC && - referencedInvokedClass != null && - (referencedInvokedClass.getAccessFlags() & AccessConstants.INTERFACE) != 0; - } - - - /** - * Returns whether the invoked method is a non-static, private synthetic - * method in an interface. - */ - boolean referencesPrivateSyntheticInterfaceMethod() - { - return (referencedInvokedClass .getAccessFlags() & AccessConstants.INTERFACE) != 0 && - (referencedInvokedMethod.getAccessFlags() & (AccessConstants.PRIVATE | - AccessConstants.SYNTHETIC)) != 0 ; - } - - - /** - * Returns whether an accessor method is needed to access - * the invoked method from the lambda class. - */ - public boolean needsAccessorMethod() - { - // We assume unknown classes don't need an accessor method. - return referencedInvokedClass != null && - new MemberFinder().findMethod(lambdaClass, - referencedInvokedClass, - invokedMethodName, - invokedMethodDesc) == null; - } - - - /** - * Returns whether the lambda expression is a method reference - * to a private constructor. - */ - public boolean referencesPrivateConstructor() - { - return invokedReferenceKind == MethodHandleConstant.REF_NEW_INVOKE_SPECIAL && - ClassConstants.METHOD_NAME_INIT.equals(invokedMethodName) && - (referencedInvokedMethod.getAccessFlags() & AccessConstants.PRIVATE) != 0; - } - - - // Small Utility methods. - - private static final String LAMBDA_METHOD_PREFIX = "lambda$"; - - private static boolean isLambdaMethod(String methodName) - { - return methodName.startsWith(LAMBDA_METHOD_PREFIX); - } -} - - From 59cf00e5c5bba4787211bee6b6fe597c55b51bfc Mon Sep 17 00:00:00 2001 From: Pranav Date: Wed, 27 Jul 2022 19:00:22 +0530 Subject: [PATCH 2/6] Delete LambdaExpressionCollector.java --- .../backport/LambdaExpressionCollector.java | 243 ------------------ 1 file changed, 243 deletions(-) delete mode 100644 base/src/main/java/proguard/backport/LambdaExpressionCollector.java diff --git a/base/src/main/java/proguard/backport/LambdaExpressionCollector.java b/base/src/main/java/proguard/backport/LambdaExpressionCollector.java deleted file mode 100644 index 09aac45cf..000000000 --- a/base/src/main/java/proguard/backport/LambdaExpressionCollector.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * ProGuard -- shrinking, optimization, obfuscation, and preverification - * of Java bytecode. - * - * Copyright (c) 2002-2020 Guardsquare NV - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * 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 General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -package proguard.backport; - -import proguard.classfile.*; -import proguard.classfile.attribute.*; -import proguard.classfile.attribute.visitor.*; -import proguard.classfile.constant.*; -import proguard.classfile.constant.visitor.*; -import proguard.classfile.util.*; -import proguard.classfile.visitor.ClassVisitor; -import proguard.util.ArrayUtil; - -import java.util.*; - -/** - * This ClassVisitor collects all lambda expressions that are defined in - * a visited class. - * - * @author Thomas Neidhart - */ -public class LambdaExpressionCollector -implements ClassVisitor, - - // Implementation interfaces. - ConstantVisitor, - AttributeVisitor, - BootstrapMethodInfoVisitor -{ - private final Map lambdaExpressions; - - private InvokeDynamicConstant referencedInvokeDynamicConstant; - private int referencedBootstrapMethodIndex; - private Clazz referencedInvokedClass; - private Method referencedInvokedMethod; - - - public LambdaExpressionCollector(Map lambdaExpressions) - { - this.lambdaExpressions = lambdaExpressions; - } - - - // Implementations for ClassVisitor. - - @Override - public void visitAnyClass(Clazz clazz) { } - - - @Override - public void visitProgramClass(ProgramClass programClass) - { - // Visit any InvokeDynamic constant. - programClass.constantPoolEntriesAccept( - new ConstantTagFilter(Constant.INVOKE_DYNAMIC, - this)); - } - - - // Implementations for ConstantVisitor. - - @Override - public void visitAnyConstant(Clazz clazz, Constant constant) {} - - - @Override - public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) - { - referencedInvokeDynamicConstant = invokeDynamicConstant; - referencedBootstrapMethodIndex = invokeDynamicConstant.getBootstrapMethodAttributeIndex(); - clazz.attributesAccept(this); - } - - - @Override - public void visitAnyMethodrefConstant(Clazz clazz, AnyMethodrefConstant anyMethodrefConstant) - { - referencedInvokedClass = anyMethodrefConstant.referencedClass; - referencedInvokedMethod = anyMethodrefConstant.referencedMethod; - } - - - // Implementations for AttributeVisitor. - - @Override - public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} - - - @Override - public void visitBootstrapMethodsAttribute(Clazz clazz, - BootstrapMethodsAttribute bootstrapMethodsAttribute) - { - bootstrapMethodsAttribute.bootstrapMethodEntryAccept(clazz, referencedBootstrapMethodIndex, this); - } - - - // Implementations for BootstrapMethodInfoVisitor. - - @Override - public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo) - { - ProgramClass programClass = (ProgramClass) clazz; - - MethodHandleConstant bootstrapMethodHandle = - (MethodHandleConstant) programClass.getConstant(bootstrapMethodInfo.u2methodHandleIndex); - - if (isLambdaMetaFactory(bootstrapMethodHandle.getClassName(clazz))) - { - String factoryMethodDescriptor = - referencedInvokeDynamicConstant.getType(clazz); - - String interfaceClassName = - ClassUtil.internalClassNameFromClassType(ClassUtil.internalMethodReturnType(factoryMethodDescriptor)); - - // Find the actual method that is being invoked. - MethodHandleConstant invokedMethodHandle = - (MethodHandleConstant) programClass.getConstant(bootstrapMethodInfo.u2methodArguments[1]); - - referencedInvokedClass = null; - referencedInvokedMethod = null; - clazz.constantPoolEntryAccept(invokedMethodHandle.u2referenceIndex, this); - - // Collect all the useful information. - LambdaExpression lambdaExpression = - new LambdaExpression(programClass, - referencedBootstrapMethodIndex, - bootstrapMethodInfo, - factoryMethodDescriptor, - new String[] { interfaceClassName }, - new String[0], - referencedInvokeDynamicConstant.getName(clazz), - getMethodTypeConstant(programClass, bootstrapMethodInfo.u2methodArguments[0]).getType(clazz), - invokedMethodHandle.getReferenceKind(), - invokedMethodHandle.getClassName(clazz), - invokedMethodHandle.getName(clazz), - invokedMethodHandle.getType(clazz), - referencedInvokedClass, - referencedInvokedMethod); - - if (isAlternateFactoryMethod(bootstrapMethodHandle.getName(clazz))) - { - int flags = - getIntegerConstant(programClass, - bootstrapMethodInfo.u2methodArguments[3]); - - // For the alternate metafactory, the optional arguments start - // at index 4. - int argumentIndex = 4; - - if ((flags & BootstrapMethodInfo.FLAG_MARKERS) != 0) - { - int markerInterfaceCount = - getIntegerConstant(programClass, - bootstrapMethodInfo.u2methodArguments[argumentIndex++]); - - for (int i = 0; i < markerInterfaceCount; i++) - { - String interfaceName = - programClass.getClassName(bootstrapMethodInfo.u2methodArguments[argumentIndex++]); - - lambdaExpression.interfaces = - ArrayUtil.add(lambdaExpression.interfaces, - lambdaExpression.interfaces.length, - interfaceName); - } - } - - if ((flags & BootstrapMethodInfo.FLAG_BRIDGES) != 0) - { - int bridgeMethodCount = - getIntegerConstant(programClass, - bootstrapMethodInfo.u2methodArguments[argumentIndex++]); - - for (int i = 0; i < bridgeMethodCount; i++) - { - MethodTypeConstant methodTypeConstant = - getMethodTypeConstant(programClass, - bootstrapMethodInfo.u2methodArguments[argumentIndex++]); - - lambdaExpression.bridgeMethodDescriptors = - ArrayUtil.add(lambdaExpression.bridgeMethodDescriptors, - lambdaExpression.bridgeMethodDescriptors.length, - methodTypeConstant.getType(programClass)); - } - } - - if ((flags & BootstrapMethodInfo.FLAG_SERIALIZABLE) != 0) - { - lambdaExpression.interfaces = - ArrayUtil.add(lambdaExpression.interfaces, - lambdaExpression.interfaces.length, - ClassConstants.NAME_JAVA_IO_SERIALIZABLE); - } - } - - lambdaExpressions.put(referencedBootstrapMethodIndex, lambdaExpression); - } - } - - // Small utility methods - private static final String NAME_JAVA_LANG_INVOKE_LAMBDA_METAFACTORY = "java/lang/invoke/LambdaMetafactory"; - - private static final String LAMBDA_ALTERNATE_METAFACTORY_METHOD = "altMetafactory"; - - private static boolean isLambdaMetaFactory(String className) - { - return NAME_JAVA_LANG_INVOKE_LAMBDA_METAFACTORY.equals(className); - } - - private static boolean isAlternateFactoryMethod(String methodName) - { - return LAMBDA_ALTERNATE_METAFACTORY_METHOD.equals(methodName); - } - - private static int getIntegerConstant(ProgramClass programClass, int constantIndex) - { - return ((IntegerConstant) programClass.getConstant(constantIndex)).getValue(); - } - - - private static MethodTypeConstant getMethodTypeConstant(ProgramClass programClass, int constantIndex) - { - return (MethodTypeConstant) programClass.getConstant(constantIndex); - } -} \ No newline at end of file From 3584ffcb7d6175625a736ea69df089e33ea2c33d Mon Sep 17 00:00:00 2001 From: PranavPurwar Date: Thu, 28 Jul 2022 18:47:16 +0530 Subject: [PATCH 3/6] Update map type to use InvokeDynamicConstant --- .../java/proguard/backport/LambdaExpressionConverter.java | 4 ++-- bin/proguard.sh | 0 bin/proguardgui.sh | 0 bin/retrace.sh | 0 examples/android-agp3-agp4/gradlew | 0 examples/android-plugin/gradlew | 0 examples/application/gradlew | 0 examples/gradle-kotlin-dsl/gradlew | 0 examples/spring-boot/gradlew | 0 gradlew | 0 10 files changed, 2 insertions(+), 2 deletions(-) mode change 100755 => 100644 bin/proguard.sh mode change 100755 => 100644 bin/proguardgui.sh mode change 100755 => 100644 bin/retrace.sh mode change 100755 => 100644 examples/android-agp3-agp4/gradlew mode change 100755 => 100644 examples/android-plugin/gradlew mode change 100755 => 100644 examples/application/gradlew mode change 100755 => 100644 examples/gradle-kotlin-dsl/gradlew mode change 100755 => 100644 examples/spring-boot/gradlew mode change 100755 => 100644 gradlew diff --git a/base/src/main/java/proguard/backport/LambdaExpressionConverter.java b/base/src/main/java/proguard/backport/LambdaExpressionConverter.java index fd3ed3ed2..7a3fc6269 100644 --- a/base/src/main/java/proguard/backport/LambdaExpressionConverter.java +++ b/base/src/main/java/proguard/backport/LambdaExpressionConverter.java @@ -59,7 +59,7 @@ public class LambdaExpressionConverter private final ExtraDataEntryNameMap extraDataEntryNameMap; private final ClassVisitor extraClassVisitor; - private final Map lambdaExpressionMap; + private final Map lambdaExpressionMap; private final CodeAttributeEditor codeAttributeEditor; private final MemberRemover memberRemover; @@ -74,7 +74,7 @@ public LambdaExpressionConverter(ClassPool programClassPool, this.extraDataEntryNameMap = extraDataEntryNameMap; this.extraClassVisitor = extraClassVisitor; - this.lambdaExpressionMap = new HashMap(); + this.lambdaExpressionMap = new HashMap(); this.codeAttributeEditor = new CodeAttributeEditor(true, true); this.memberRemover = new MemberRemover(); } diff --git a/bin/proguard.sh b/bin/proguard.sh old mode 100755 new mode 100644 diff --git a/bin/proguardgui.sh b/bin/proguardgui.sh old mode 100755 new mode 100644 diff --git a/bin/retrace.sh b/bin/retrace.sh old mode 100755 new mode 100644 diff --git a/examples/android-agp3-agp4/gradlew b/examples/android-agp3-agp4/gradlew old mode 100755 new mode 100644 diff --git a/examples/android-plugin/gradlew b/examples/android-plugin/gradlew old mode 100755 new mode 100644 diff --git a/examples/application/gradlew b/examples/application/gradlew old mode 100755 new mode 100644 diff --git a/examples/gradle-kotlin-dsl/gradlew b/examples/gradle-kotlin-dsl/gradlew old mode 100755 new mode 100644 diff --git a/examples/spring-boot/gradlew b/examples/spring-boot/gradlew old mode 100755 new mode 100644 diff --git a/gradlew b/gradlew old mode 100755 new mode 100644 From d6d1221002746be70a9be0f2ded9c28326033a72 Mon Sep 17 00:00:00 2001 From: PranavPurwar Date: Thu, 28 Jul 2022 13:54:44 +0000 Subject: [PATCH 4/6] revert file permission changes --- bin/proguard.sh | 0 bin/proguardgui.sh | 0 bin/retrace.sh | 0 examples/android-agp3-agp4/gradlew | 0 examples/android-plugin/gradlew | 0 examples/application/gradlew | 0 examples/gradle-kotlin-dsl/gradlew | 0 examples/spring-boot/gradlew | 0 gradlew | 0 9 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 bin/proguard.sh mode change 100644 => 100755 bin/proguardgui.sh mode change 100644 => 100755 bin/retrace.sh mode change 100644 => 100755 examples/android-agp3-agp4/gradlew mode change 100644 => 100755 examples/android-plugin/gradlew mode change 100644 => 100755 examples/application/gradlew mode change 100644 => 100755 examples/gradle-kotlin-dsl/gradlew mode change 100644 => 100755 examples/spring-boot/gradlew mode change 100644 => 100755 gradlew diff --git a/bin/proguard.sh b/bin/proguard.sh old mode 100644 new mode 100755 diff --git a/bin/proguardgui.sh b/bin/proguardgui.sh old mode 100644 new mode 100755 diff --git a/bin/retrace.sh b/bin/retrace.sh old mode 100644 new mode 100755 diff --git a/examples/android-agp3-agp4/gradlew b/examples/android-agp3-agp4/gradlew old mode 100644 new mode 100755 diff --git a/examples/android-plugin/gradlew b/examples/android-plugin/gradlew old mode 100644 new mode 100755 diff --git a/examples/application/gradlew b/examples/application/gradlew old mode 100644 new mode 100755 diff --git a/examples/gradle-kotlin-dsl/gradlew b/examples/gradle-kotlin-dsl/gradlew old mode 100644 new mode 100755 diff --git a/examples/spring-boot/gradlew b/examples/spring-boot/gradlew old mode 100644 new mode 100755 diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From f4308f9d0652030b17f3b24e16df5e0490e2ced4 Mon Sep 17 00:00:00 2001 From: James Hamilton Date: Thu, 28 Jul 2022 15:56:06 +0200 Subject: [PATCH 5/6] Change key type --- .../proguard/backport/LambdaExpressionConverter.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/base/src/main/java/proguard/backport/LambdaExpressionConverter.java b/base/src/main/java/proguard/backport/LambdaExpressionConverter.java index 7a3fc6269..56d671603 100644 --- a/base/src/main/java/proguard/backport/LambdaExpressionConverter.java +++ b/base/src/main/java/proguard/backport/LambdaExpressionConverter.java @@ -60,8 +60,8 @@ public class LambdaExpressionConverter private final ClassVisitor extraClassVisitor; private final Map lambdaExpressionMap; - private final CodeAttributeEditor codeAttributeEditor; - private final MemberRemover memberRemover; + private final CodeAttributeEditor codeAttributeEditor; + private final MemberRemover memberRemover; public LambdaExpressionConverter(ClassPool programClassPool, @@ -74,7 +74,7 @@ public LambdaExpressionConverter(ClassPool programClassPool, this.extraDataEntryNameMap = extraDataEntryNameMap; this.extraClassVisitor = extraClassVisitor; - this.lambdaExpressionMap = new HashMap(); + this.lambdaExpressionMap = new HashMap<>(); this.codeAttributeEditor = new CodeAttributeEditor(true, true); this.memberRemover = new MemberRemover(); } @@ -171,10 +171,9 @@ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute c InvokeDynamicConstant invokeDynamicConstant = (InvokeDynamicConstant) programClass.getConstant(constantInstruction.constantIndex); - int bootstrapMethodIndex = invokeDynamicConstant.getBootstrapMethodAttributeIndex(); - if (lambdaExpressionMap.containsKey(bootstrapMethodIndex)) + if (lambdaExpressionMap.containsKey(invokeDynamicConstant)) { - LambdaExpression lambdaExpression = lambdaExpressionMap.get(bootstrapMethodIndex); + LambdaExpression lambdaExpression = lambdaExpressionMap.get(invokeDynamicConstant); String lambdaClassName = lambdaExpression.getLambdaClassName(); InstructionSequenceBuilder builder = From bd6275c4704e346397fef46f5a542e29b84645f7 Mon Sep 17 00:00:00 2001 From: James Hamilton Date: Thu, 28 Jul 2022 15:56:54 +0200 Subject: [PATCH 6/6] Add test --- .../backport/LambdaExpressionConverterTest.kt | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 base/src/test/kotlin/proguard/backport/LambdaExpressionConverterTest.kt diff --git a/base/src/test/kotlin/proguard/backport/LambdaExpressionConverterTest.kt b/base/src/test/kotlin/proguard/backport/LambdaExpressionConverterTest.kt new file mode 100644 index 000000000..b7344267b --- /dev/null +++ b/base/src/test/kotlin/proguard/backport/LambdaExpressionConverterTest.kt @@ -0,0 +1,78 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2022 Guardsquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * 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 General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package proguard.backport + +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import proguard.classfile.attribute.visitor.AllAttributeVisitor +import proguard.classfile.editor.InstructionSequenceBuilder +import proguard.classfile.instruction.visitor.AllInstructionVisitor +import proguard.classfile.util.InstructionSequenceMatcher +import proguard.classfile.visitor.ClassPrinter +import proguard.io.ExtraDataEntryNameMap +import testutils.ClassPoolBuilder +import testutils.JavaSource + +class LambdaExpressionConverterTest : FreeSpec({ + "Given a lambda expression" - { + val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource( + JavaSource( + "Test.java", + """ + import java.util.function.Function; + public class Test { + public void test() { + foo(s -> s.length()); + } + public void foo(Function function) { + System.out.println(function.apply("Hello")); + } + } + """.trimIndent() + ) + ) + programClassPool.classesAccept(ClassPrinter()) + "When backported" - { + val lambdaExpressionConvertor = LambdaExpressionConverter(programClassPool, libraryClassPool, ExtraDataEntryNameMap(), null) + programClassPool.classesAccept("Test", lambdaExpressionConvertor) + programClassPool.classesAccept(ClassPrinter()) + "Then a new class should be created" { + programClassPool.getClass("Test\$\$Lambda\$0") shouldNotBe null + } + + "Then the invocation should call the backported lambda" { + val clazz = programClassPool.getClass("Test") + val builder = InstructionSequenceBuilder() + builder + .aload_0() + .getstatic("Test\$\$Lambda\$0", "INSTANCE", "LTest\$\$Lambda\$0;") + .invokevirtual("Test", "foo", "(Ljava/util/function/Function;)V") + .return_() + + val matcher = InstructionSequenceMatcher(builder.constants(), builder.instructions()) + clazz.methodAccept("test", "()V", AllAttributeVisitor(AllInstructionVisitor(matcher))) + matcher.isMatching shouldBe true + } + } + } +})