From 821e2025e945f6c5bfe500519cffda849ae93b55 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Wed, 16 Apr 2025 15:26:08 +0900 Subject: [PATCH 01/23] Fix typo in Javadoc in SymbolTable --- asm/src/main/java/org/objectweb/asm/SymbolTable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asm/src/main/java/org/objectweb/asm/SymbolTable.java b/asm/src/main/java/org/objectweb/asm/SymbolTable.java index c3b17957..d4c741a4 100644 --- a/asm/src/main/java/org/objectweb/asm/SymbolTable.java +++ b/asm/src/main/java/org/objectweb/asm/SymbolTable.java @@ -1473,7 +1473,7 @@ private static final class LabelEntry { /** * Another entry (and so on recursively) having the same hash code (modulo the size of {@link - * SymbolTable#labelEntries}}) as this one. + * SymbolTable#labelEntries}) as this one. */ LabelEntry next; From b88d42f1a46d44b432726efe414d54605cea7c58 Mon Sep 17 00:00:00 2001 From: forax Date: Sat, 7 Jun 2025 10:40:38 +0200 Subject: [PATCH 02/23] Add support of Java 26 --- asm-util/src/main/java/org/objectweb/asm/util/ASMifier.java | 1 + asm/src/main/java/org/objectweb/asm/ClassReader.java | 2 +- asm/src/main/java/org/objectweb/asm/Opcodes.java | 1 + asm/src/test/java/org/objectweb/asm/ConstantsTest.java | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/asm-util/src/main/java/org/objectweb/asm/util/ASMifier.java b/asm-util/src/main/java/org/objectweb/asm/util/ASMifier.java index 99e2bc0d..c4b24cae 100644 --- a/asm-util/src/main/java/org/objectweb/asm/util/ASMifier.java +++ b/asm-util/src/main/java/org/objectweb/asm/util/ASMifier.java @@ -115,6 +115,7 @@ public class ASMifier extends Printer { classVersions.put(Opcodes.V23, "V23"); classVersions.put(Opcodes.V24, "V24"); classVersions.put(Opcodes.V25, "V25"); + classVersions.put(Opcodes.V26, "V26"); CLASS_VERSIONS = Collections.unmodifiableMap(classVersions); } diff --git a/asm/src/main/java/org/objectweb/asm/ClassReader.java b/asm/src/main/java/org/objectweb/asm/ClassReader.java index 5f66ed2b..a807c047 100644 --- a/asm/src/main/java/org/objectweb/asm/ClassReader.java +++ b/asm/src/main/java/org/objectweb/asm/ClassReader.java @@ -195,7 +195,7 @@ public ClassReader( this.b = classFileBuffer; // Check the class' major_version. This field is after the magic and minor_version fields, which // use 4 and 2 bytes respectively. - if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V25) { + if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V26) { throw new IllegalArgumentException( "Unsupported class file major version " + readShort(classFileOffset + 6)); } diff --git a/asm/src/main/java/org/objectweb/asm/Opcodes.java b/asm/src/main/java/org/objectweb/asm/Opcodes.java index 4ed95869..5b51acf4 100644 --- a/asm/src/main/java/org/objectweb/asm/Opcodes.java +++ b/asm/src/main/java/org/objectweb/asm/Opcodes.java @@ -291,6 +291,7 @@ public interface Opcodes { int V23 = 0 << 16 | 67; int V24 = 0 << 16 | 68; int V25 = 0 << 16 | 69; + int V26 = 0 << 16 | 70; /** * Version flag indicating that the class is using 'preview' features. diff --git a/asm/src/test/java/org/objectweb/asm/ConstantsTest.java b/asm/src/test/java/org/objectweb/asm/ConstantsTest.java index 543ba044..c746e2e8 100644 --- a/asm/src/test/java/org/objectweb/asm/ConstantsTest.java +++ b/asm/src/test/java/org/objectweb/asm/ConstantsTest.java @@ -258,6 +258,7 @@ private static ConstantType getType(final Field field) { case "V23": case "V24": case "V25": + case "V26": return ConstantType.CLASS_VERSION; case "ACC_PUBLIC": case "ACC_PRIVATE": From dfefb0b80b57309927875803ef80895b835bee9f Mon Sep 17 00:00:00 2001 From: Remi Forax Date: Mon, 23 Jun 2025 08:34:24 +0000 Subject: [PATCH 03/23] Use "Visits" instead of "Visit" in the javadoc of the methods to be coherent ... --- .../main/java/org/objectweb/asm/ModuleVisitor.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/asm/src/main/java/org/objectweb/asm/ModuleVisitor.java b/asm/src/main/java/org/objectweb/asm/ModuleVisitor.java index 9a566f3d..d03e4c39 100644 --- a/asm/src/main/java/org/objectweb/asm/ModuleVisitor.java +++ b/asm/src/main/java/org/objectweb/asm/ModuleVisitor.java @@ -93,7 +93,7 @@ public ModuleVisitor getDelegate() { } /** - * Visit the main class of the current module. + * Visits the main class of the current module. * * @param mainClass the internal name of the main class of the current module (see {@link * Type#getInternalName()}). @@ -105,7 +105,7 @@ public void visitMainClass(final String mainClass) { } /** - * Visit a package of the current module. + * Visits a package of the current module. * * @param packaze the internal name of a package (see {@link Type#getInternalName()}). */ @@ -130,7 +130,7 @@ public void visitRequire(final String module, final int access, final String ver } /** - * Visit an exported package of the current module. + * Visits an exported package of the current module. * * @param packaze the internal name of the exported package (see {@link Type#getInternalName()}). * @param access the access flag of the exported package, valid values are among {@code @@ -145,7 +145,7 @@ public void visitExport(final String packaze, final int access, final String... } /** - * Visit an open package of the current module. + * Visits an open package of the current module. * * @param packaze the internal name of the opened package (see {@link Type#getInternalName()}). * @param access the access flag of the opened package, valid values are among {@code @@ -160,7 +160,7 @@ public void visitOpen(final String packaze, final int access, final String... mo } /** - * Visit a service used by the current module. The name must be the internal name of an interface + * Visits a service used by the current module. The name must be the internal name of an interface * or a class. * * @param service the internal name of the service (see {@link Type#getInternalName()}). @@ -172,7 +172,7 @@ public void visitUse(final String service) { } /** - * Visit an implementation of a service. + * Visits an implementation of a service. * * @param service the internal name of the service (see {@link Type#getInternalName()}). * @param providers the internal names (see {@link Type#getInternalName()}) of the implementations From e75e722f57b4e4d7566f15ae57faa05e38f608c0 Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Mon, 23 Jun 2025 16:31:15 +0000 Subject: [PATCH 04/23] Update Sonar Gradle plugin to latest version. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f618ea89..bfa9d0ff 100644 --- a/build.gradle +++ b/build.gradle @@ -33,7 +33,7 @@ buildscript { plugins { id 'com.diffplug.spotless' version "6.23.3" apply false } plugins { id 'me.champeau.jmh' version '0.7.2' apply false } -plugins { id 'org.sonarqube' version '4.3.1.3277' apply false } +plugins { id 'org.sonarqube' version '6.2.0.5505' apply false } description = 'ASM, a very small and fast Java bytecode manipulation framework' From 2ce241ad5babf9452c73833ec0e880d90d980ad9 Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sun, 29 Jun 2025 10:31:42 +0200 Subject: [PATCH 05/23] Do not filter out ACC_SUPER when printing the raw access flags of inner classes. --- asm-util/src/main/java/org/objectweb/asm/util/Textifier.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asm-util/src/main/java/org/objectweb/asm/util/Textifier.java b/asm-util/src/main/java/org/objectweb/asm/util/Textifier.java index 17dbcbf3..4df14f1f 100644 --- a/asm-util/src/main/java/org/objectweb/asm/util/Textifier.java +++ b/asm-util/src/main/java/org/objectweb/asm/util/Textifier.java @@ -322,7 +322,7 @@ public void visitInnerClass( final String name, final String outerName, final String innerName, final int access) { stringBuilder.setLength(0); stringBuilder.append(tab); - appendRawAccess(access & ~Opcodes.ACC_SUPER); + appendRawAccess(access); stringBuilder.append(tab); appendAccess(access); stringBuilder.append("INNERCLASS "); From 0b346044147b5129f31086f95c9a8e2fe9990f2c Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sun, 6 Jul 2025 10:45:03 +0200 Subject: [PATCH 06/23] Use 'sha-1' algorithm instead of 'sha'. --- .../java/org/objectweb/asm/commons/SerialVersionUIDAdder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/SerialVersionUIDAdder.java b/asm-commons/src/main/java/org/objectweb/asm/commons/SerialVersionUIDAdder.java index 7daaaec2..50553c20 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/SerialVersionUIDAdder.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/SerialVersionUIDAdder.java @@ -423,7 +423,7 @@ protected long computeSVUID() throws IOException { // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility). protected byte[] computeSHAdigest(final byte[] value) { try { - return MessageDigest.getInstance("SHA").digest(value); + return MessageDigest.getInstance("SHA-1").digest(value); } catch (NoSuchAlgorithmException e) { throw new UnsupportedOperationException(e); } From ad4dc0f7b2735f48db035849a7d7788f17215021 Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sun, 31 Aug 2025 09:42:14 +0200 Subject: [PATCH 07/23] Add a new mapInvokeDynamicMethodName method. --- .../objectweb/asm/commons/MethodRemapper.java | 11 +- .../org/objectweb/asm/commons/Remapper.java | 77 ++++++++++- .../objectweb/asm/commons/SimpleRemapper.java | 58 ++++++++ .../asm/commons/ClassRemapperTest.java | 128 ++++++++++++++++-- .../asm/commons/SimpleRemapperTest.java | 16 ++- 5 files changed, 269 insertions(+), 21 deletions(-) diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/MethodRemapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/MethodRemapper.java index c889d47d..56c35be5 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/MethodRemapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/MethodRemapper.java @@ -170,17 +170,26 @@ public void visitMethodInsn( } @Override + @SuppressWarnings("deprecation") public void visitInvokeDynamicInsn( final String name, final String descriptor, final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments) { + String remappedName; + if (remapper.api == 0) { + remappedName = remapper.mapInvokeDynamicMethodName(name, descriptor); + } else { + remappedName = + remapper.mapInvokeDynamicMethodName( + name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); + } Object[] remappedBootstrapMethodArguments = new Object[bootstrapMethodArguments.length]; for (int i = 0; i < bootstrapMethodArguments.length; ++i) { remappedBootstrapMethodArguments[i] = remapper.mapValue(bootstrapMethodArguments[i]); } super.visitInvokeDynamicInsn( - remapper.mapInvokeDynamicMethodName(name, descriptor), + remappedName, remapper.mapMethodDesc(descriptor), (Handle) remapper.mapValue(bootstrapMethodHandle), remappedBootstrapMethodArguments); diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java index 3004b4d2..f619750e 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java @@ -43,6 +43,41 @@ */ public abstract class Remapper { + /** + * The ASM API version supported by this remapper, or 0 for instances created with the deprecated + * constructor. + */ + final int api; + + /** + * Creates a new {@link Remapper}. + * + * @deprecated use {@link #Remapper(int)} instead. + */ + @Deprecated + public Remapper() { + this.api = 0; + } + + /** + * Creates a new {@link Remapper}. + * + * @param api the ASM API version supported by this remapper. Must be one of the {@code + * ASM}x values in {@link Opcodes}. + */ + public Remapper(final int api) { + if (api != Opcodes.ASM9 + && api != Opcodes.ASM8 + && api != Opcodes.ASM7 + && api != Opcodes.ASM6 + && api != Opcodes.ASM5 + && api != Opcodes.ASM4 + && api != Opcodes.ASM10_EXPERIMENTAL) { + throw new IllegalArgumentException("Unsupported api " + api); + } + this.api = api; + } + /** * Returns the given descriptor, remapped with {@link #map(String)}. * @@ -177,17 +212,26 @@ public Object mapValue(final Object value) { } if (value instanceof ConstantDynamic) { ConstantDynamic constantDynamic = (ConstantDynamic) value; + String name = constantDynamic.getName(); + String descriptor = constantDynamic.getDescriptor(); + Handle bootstrapMethod = constantDynamic.getBootstrapMethod(); int bootstrapMethodArgumentCount = constantDynamic.getBootstrapMethodArgumentCount(); + Object[] bootstrapMethodArguments = new Object[bootstrapMethodArgumentCount]; Object[] remappedBootstrapMethodArguments = new Object[bootstrapMethodArgumentCount]; for (int i = 0; i < bootstrapMethodArgumentCount; ++i) { - remappedBootstrapMethodArguments[i] = - mapValue(constantDynamic.getBootstrapMethodArgument(i)); + bootstrapMethodArguments[i] = constantDynamic.getBootstrapMethodArgument(i); + remappedBootstrapMethodArguments[i] = mapValue(bootstrapMethodArguments[i]); + } + if (api == 0) { + name = mapInvokeDynamicMethodName(name, descriptor); + } else { + name = + mapInvokeDynamicMethodName(name, descriptor, bootstrapMethod, bootstrapMethodArguments); } - String descriptor = constantDynamic.getDescriptor(); return new ConstantDynamic( - mapInvokeDynamicMethodName(constantDynamic.getName(), descriptor), + name, mapDesc(descriptor), - (Handle) mapValue(constantDynamic.getBootstrapMethod()), + (Handle) mapValue(bootstrapMethod), remappedBootstrapMethodArguments); } return value; @@ -316,11 +360,34 @@ public String mapMethodName(final String owner, final String name, final String * @param name the name of the method. * @param descriptor the descriptor of the method. * @return the new name of the method. + * @deprecated use {@link #mapInvokeDynamicMethodName(String, String, Handle, Object...)} instead. */ + @Deprecated public String mapInvokeDynamicMethodName(final String name, final String descriptor) { return name; } + /** + * Maps an invokedynamic or a constant dynamic method name to its new name. The default + * implementation of this method returns the given name, unchanged. Subclasses can override. + * + * @param name the name of the method. + * @param descriptor the descriptor of the method. + * @param bootstrapMethodHandle the bootstrap method. + * @param bootstrapMethodArguments the bootstrap method constant arguments. Each argument must be + * an {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link + * Type}, {@link Handle} or {@link ConstantDynamic} value. This method is allowed to modify + * the content of the array so a caller should expect that this array may change. + * @return the new name of the method. + */ + public String mapInvokeDynamicMethodName( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + return name; + } + /** * Maps a record component name to its new name. The default implementation of this method returns * the given name, unchanged. Subclasses can override. diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java index 44f3442d..fdae678c 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java @@ -30,6 +30,8 @@ import java.util.Collections; import java.util.Map; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Opcodes; /** * A {@link Remapper} using a {@link Map} to define its mapping. @@ -58,11 +60,40 @@ public class SimpleRemapper extends Remapper { *
  • for internal names, the key is the old internal name, and the value is the new * internal name (see {@link org.objectweb.asm.Type#getInternalName()}). * + * + * @deprecated use {@link #SimpleRemapper(int, Map)} instead. */ + @Deprecated public SimpleRemapper(final Map mapping) { this.mapping = mapping; } + /** + * Constructs a new {@link SimpleRemapper} with the given mapping. + * + * @param api the ASM API version supported by this remapper. Must be one of the {@code + * ASM}x values in {@link Opcodes}. + * @param mapping a map specifying a remapping as follows: + *
      + *
    • for method names, the key is the owner, name and descriptor of the method (in the + * form <owner>.<name><descriptor>), and the value is the new method + * name. + *
    • for invokedynamic method names, the key is the name and descriptor of the method (in + * the form .<name><descriptor>), and the value is the new method name. + *
    • for field names, the key is the owner and name of the field or attribute (in the form + * <owner>.<name>), and the value is the new field name. + *
    • for attribute names, the key is the annotation descriptor and the name of the + * attribute (in the form <descriptor>.<name>), and the value is the new + * attribute name. + *
    • for internal names, the key is the old internal name, and the value is the new + * internal name (see {@link org.objectweb.asm.Type#getInternalName()}). + *
    + */ + public SimpleRemapper(final int api, final Map mapping) { + super(api); + this.mapping = mapping; + } + /** * Constructs a new {@link SimpleRemapper} with the given mapping. * @@ -70,11 +101,28 @@ public SimpleRemapper(final Map mapping) { * #SimpleRemapper(Map)} for the format of these keys). * @param newName the new method, field or internal name (see {@link * org.objectweb.asm.Type#getInternalName()}). + * @deprecated use {@link #SimpleRemapper(int, String, String)} instead. */ + @Deprecated public SimpleRemapper(final String oldName, final String newName) { this.mapping = Collections.singletonMap(oldName, newName); } + /** + * Constructs a new {@link SimpleRemapper} with the given mapping. + * + * @param api the ASM API version supported by this remapper. Must be one of the {@code + * ASM}x values in {@link Opcodes}. + * @param oldName the key corresponding to a method, field or internal name (see {@link + * #SimpleRemapper(Map)} for the format of these keys). + * @param newName the new method, field or internal name (see {@link + * org.objectweb.asm.Type#getInternalName()}). + */ + public SimpleRemapper(final int api, final String oldName, final String newName) { + super(api); + this.mapping = Collections.singletonMap(oldName, newName); + } + @Override public String mapMethodName(final String owner, final String name, final String descriptor) { String remappedName = map(owner + '.' + name + descriptor); @@ -87,6 +135,16 @@ public String mapInvokeDynamicMethodName(final String name, final String descrip return remappedName == null ? name : remappedName; } + @Override + public String mapInvokeDynamicMethodName( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + String remappedName = map('.' + name + descriptor); + return remappedName == null ? name : remappedName; + } + @Override public String mapAnnotationAttributeName(final String descriptor, final String name) { String remappedName = map(descriptor + '.' + name); diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/ClassRemapperTest.java b/asm-commons/src/test/java/org/objectweb/asm/commons/ClassRemapperTest.java index 97686d77..2774cca4 100644 --- a/asm-commons/src/test/java/org/objectweb/asm/commons/ClassRemapperTest.java +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/ClassRemapperTest.java @@ -67,7 +67,9 @@ class ClassRemapperTest extends AsmTest { void testVisit() { ClassNode classNode = new ClassNode(); ClassRemapper classRemapper = - new ClassRemapper(classNode, new SimpleRemapper("pkg/C", "new/pkg/C")); + new ClassRemapper( + classNode, + new SimpleRemapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL, "pkg/C", "new/pkg/C")); classRemapper.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, "pkg/C", null, "java/lang/Object", null); @@ -80,7 +82,7 @@ void testVisitAnnotation() { ClassRemapper remapper = new ClassRemapper( classNode, - new Remapper() { + new Remapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL) { @Override public String mapAnnotationAttributeName(final String descriptor, final String name) { if ("Lpkg/A;".equals(descriptor)) { @@ -102,7 +104,7 @@ void testVisitInnerClass() { ClassRemapper remapper = new ClassRemapper( classNode, - new Remapper() { + new Remapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL) { @Override public String map(final String internalName) { if ("pkg/C".equals(internalName)) { @@ -129,7 +131,7 @@ void testVisitInnerClass_localInnerClass() { ClassRemapper remapper = new ClassRemapper( classNode, - new Remapper() { + new Remapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL) { @Override public String map(final String internalName) { if ("pkg/C".equals(internalName)) { @@ -156,7 +158,7 @@ void testVisitInnerClass_specialRemap() { ClassRemapper remapper = new ClassRemapper( classNode, - new Remapper() { + new Remapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL) { @Override public String map(final String internalName) { if ("pkg/C".equals(internalName)) { @@ -183,7 +185,7 @@ void testVisitAttribute_moduleHashes() { ClassRemapper classRemapper = new ClassRemapper( classNode, - new Remapper() { + new Remapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL) { @Override public String mapModuleName(final String name) { return "new." + name; @@ -200,7 +202,8 @@ public String mapModuleName(final String name) { } @Test - void testVisitLdcInsn_constantDynamic() { + @SuppressWarnings("deprecation") + void testVisitLdcInsn_constantDynamic_deprecated() { ClassNode classNode = new ClassNode(); ClassRemapper classRemapper = new ClassRemapper( @@ -241,13 +244,63 @@ public String map(final String internalName) { } @Test - void testInvokeDynamicInsn_field() { + void testVisitLdcInsn_constantDynamic() { + ClassNode classNode = new ClassNode(); + ClassRemapper classRemapper = + new ClassRemapper( + /* latest api */ Opcodes.ASM9, + classNode, + new Remapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL) { + @Override + public String mapInvokeDynamicMethodName( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + return "new." + name; + } + + @Override + public String map(final String internalName) { + if (internalName.equals("java/lang/String")) { + return "java/lang/Integer"; + } + return internalName; + } + }) { + /* inner class so it can access the protected constructor */ + }; + classRemapper.visit(Opcodes.V11, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null); + MethodVisitor methodVisitor = + classRemapper.visitMethod(Opcodes.ACC_PUBLIC, "hello", "()V", null, null); + methodVisitor.visitCode(); + + methodVisitor.visitLdcInsn( + new ConstantDynamic( + "foo", + "Ljava/lang/String;", + new Handle(Opcodes.H_INVOKESTATIC, "BSMHost", "bsm", "()Ljava/lang/String;", false))); + + ConstantDynamic constantDynamic = + (ConstantDynamic) ((LdcInsnNode) classNode.methods.get(0).instructions.get(0)).cst; + assertEquals("new.foo", constantDynamic.getName()); + assertEquals("Ljava/lang/Integer;", constantDynamic.getDescriptor()); + assertEquals("()Ljava/lang/Integer;", constantDynamic.getBootstrapMethod().getDesc()); + } + + @Test + void testInvokeDynamicInsn_field_deprecated() { ClassNode classNode = new ClassNode(); ClassRemapper classRemapper = new ClassRemapper( /* latest api */ Opcodes.ASM9, classNode, new Remapper() { + @Override + public String mapInvokeDynamicMethodName(final String name, final String descriptor) { + return "new." + name; + } + @Override public String mapFieldName( final String owner, final String name, final String descriptor) { @@ -269,6 +322,49 @@ public String mapFieldName( InvokeDynamicInsnNode invokeDynamic = (InvokeDynamicInsnNode) classNode.methods.get(0).instructions.get(0); + assertEquals("new.foo", invokeDynamic.name); + assertEquals("demo", invokeDynamic.bsm.getName()); + } + + @Test + void testInvokeDynamicInsn_field() { + ClassNode classNode = new ClassNode(); + ClassRemapper classRemapper = + new ClassRemapper( + /* latest api */ Opcodes.ASM9, + classNode, + new Remapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL) { + @Override + public String mapInvokeDynamicMethodName( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + return "new." + name; + } + + @Override + public String mapFieldName( + final String owner, final String name, final String descriptor) { + if ("a".equals(name)) { + return "demo"; + } + return name; + } + }); + classRemapper.visit(Opcodes.V11, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null); + MethodVisitor methodVisitor = + classRemapper.visitMethod(Opcodes.ACC_PUBLIC, "hello", "()V", null, null); + methodVisitor.visitCode(); + + methodVisitor.visitInvokeDynamicInsn( + "foo", + "()Ljava/lang/String;", + new Handle(Opcodes.H_GETFIELD, "pkg/B", "a", "Ljava/lang/String;", false)); + + InvokeDynamicInsnNode invokeDynamic = + (InvokeDynamicInsnNode) classNode.methods.get(0).instructions.get(0); + assertEquals("new.foo", invokeDynamic.name); assertEquals("demo", invokeDynamic.bsm.getName()); } @@ -281,7 +377,10 @@ void testEmptyClassRemapper_precompiledClass( ClassReader classReader = new ClassReader(classFile); ClassWriter classWriter = new ClassWriter(0); ClassRemapper classRemapper = - newClassRemapper(apiParameter.value(), classWriter, new SimpleRemapper(Map.of())); + newClassRemapper( + apiParameter.value(), + classWriter, + new SimpleRemapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL, Map.of())); Executable accept = () -> classReader.accept(classRemapper, new Attribute[] {new CodeComment()}, 0); @@ -315,7 +414,9 @@ void testEmptyClassRemapper_innerClassNames() { byte[] classFile = classFileWriter.toByteArray(); ClassReader classReader = new ClassReader(classFile); ClassWriter classWriter = new ClassWriter(0); - ClassRemapper classRemapper = new ClassRemapper(classWriter, new SimpleRemapper(Map.of())); + ClassRemapper classRemapper = + new ClassRemapper( + classWriter, new SimpleRemapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL, Map.of())); classReader.accept(classRemapper, new Attribute[] {new CodeComment()}, 0); @@ -406,6 +507,7 @@ static class UpperCaseRemapper extends Remapper { private final String remappedInternalClassName; UpperCaseRemapper(final String internalClassName) { + super(/* latest */ Opcodes.ASM10_EXPERIMENTAL); this.internalClassName = internalClassName; this.remappedInternalClassName = internalClassName.equals("module-info") @@ -440,7 +542,11 @@ public String mapMethodName(final String owner, final String name, final String } @Override - public String mapInvokeDynamicMethodName(final String name, final String descriptor) { + public String mapInvokeDynamicMethodName( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { return name.toUpperCase(LOCALE); } diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/SimpleRemapperTest.java b/asm-commons/src/test/java/org/objectweb/asm/commons/SimpleRemapperTest.java index 50497e13..6e5f617f 100644 --- a/asm-commons/src/test/java/org/objectweb/asm/commons/SimpleRemapperTest.java +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/SimpleRemapperTest.java @@ -31,6 +31,7 @@ import java.util.Map; import org.junit.jupiter.api.Test; +import org.objectweb.asm.Opcodes; /** * Unit tests for {@link SimpleRemapper}. @@ -42,7 +43,9 @@ class SimpleRemapperTest { @Test void testMapSignature_remapParentOnly_nestedClassExtends() { String inputSignature = "LOuter.Inner;"; - Remapper remapper = new SimpleRemapper(Map.of("Outer", "RenamedOuter")); + Remapper remapper = + new SimpleRemapper( + /* latest */ Opcodes.ASM10_EXPERIMENTAL, Map.of("Outer", "RenamedOuter")); String remappedSignature = remapper.mapSignature(inputSignature, false); @@ -52,7 +55,9 @@ void testMapSignature_remapParentOnly_nestedClassExtends() { @Test void testMapSignature_remapChildOnly_nestedClassExtends() { String inputSignature = "LOuter.Inner;"; - Remapper remapper = new SimpleRemapper(Map.of("Outer$Inner", "Outer$RenamedInner")); + Remapper remapper = + new SimpleRemapper( + /* latest */ Opcodes.ASM10_EXPERIMENTAL, Map.of("Outer$Inner", "Outer$RenamedInner")); String remappedSignature = remapper.mapSignature(inputSignature, false); @@ -62,7 +67,10 @@ void testMapSignature_remapChildOnly_nestedClassExtends() { @Test void testMapSignature_remapChildOnly_nestedClassExtends_identifiersWithDollarSign() { String inputSignature = "LOuter.Inner$1;"; - Remapper remapper = new SimpleRemapper(Map.of("Outer$Inner$1", "Outer$RenamedInner$1")); + Remapper remapper = + new SimpleRemapper( + /* latest */ Opcodes.ASM10_EXPERIMENTAL, + Map.of("Outer$Inner$1", "Outer$RenamedInner$1")); String remappedSignature = remapper.mapSignature(inputSignature, false); @@ -74,7 +82,7 @@ void testMapSignature_remapBothParentAndChild_nestedClassExtends() { String inputSignature = "LOuter.Inner;"; Map mapping = Map.of("Outer", "RenamedOuter", "Outer$Inner", "RenamedOuter$RenamedInner"); - Remapper remapper = new SimpleRemapper(mapping); + Remapper remapper = new SimpleRemapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL, mapping); String remappedSignature = remapper.mapSignature(inputSignature, false); From d9813a9a351b575c3634212727b5cccfc082a5a9 Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Sat, 13 Sep 2025 09:35:10 +0000 Subject: [PATCH 08/23] Improve `How to Contribute` in README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 890fc6a8..bf50faa2 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,14 @@ with [googleJavaFormat](https://github.com/google/google-java-format), [Checkstyle](https://checkstyle.sourceforge.io) and [PMD](https://pmd.github.io). +To fix code formatting errors, you can run the reformat task: + +```shell +./gradle/gradlew spotlessApply +``` + +For more details, see the [Developer Guide](https://asm.ow2.io/developer-guide.html) + ## Reporting Issues If you encounter any issues with the ASM project, please create a new issue From 1eda731c93dc164674e12ff5a9f53bec71d3dce4 Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sun, 21 Sep 2025 10:17:48 +0200 Subject: [PATCH 09/23] Add a jdk8.AllLambdas test case and fix two missing ASM API checks for invokedynamic. --- .../asm/commons/InstructionAdapter.java | 3 + .../java/org/objectweb/asm/test/AsmTest.java | 1 + .../jdk8/AllLambdas$InnerClass.class | Bin 0 -> 260 bytes .../src/main/resources/jdk8/AllLambdas.class | Bin 0 -> 2298 bytes .../src/resources/java/jdk8/AllLambdas.java | 54 ++++ .../org/objectweb/asm/tree/MethodNode.java | 2 + .../src/test/resources/jdk8.AllLambdas.txt | 243 ++++++++++++++++++ .../org/objectweb/asm/ClassReaderTest.java | 3 +- build.gradle | 11 +- 9 files changed, 311 insertions(+), 6 deletions(-) create mode 100644 asm-test/src/main/resources/jdk8/AllLambdas$InnerClass.class create mode 100644 asm-test/src/main/resources/jdk8/AllLambdas.class create mode 100644 asm-test/src/resources/java/jdk8/AllLambdas.java create mode 100644 asm-util/src/test/resources/jdk8.AllLambdas.txt diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/InstructionAdapter.java b/asm-commons/src/main/java/org/objectweb/asm/commons/InstructionAdapter.java index afe8d689..84f9579a 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/InstructionAdapter.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/InstructionAdapter.java @@ -544,6 +544,9 @@ public void visitInvokeDynamicInsn( final String descriptor, final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments) { + if (api < Opcodes.ASM5) { + throw new UnsupportedOperationException("This feature requires ASM5"); + } invokedynamic(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); } diff --git a/asm-test/src/main/java/org/objectweb/asm/test/AsmTest.java b/asm-test/src/main/java/org/objectweb/asm/test/AsmTest.java index ee0ad099..7320e211 100644 --- a/asm-test/src/main/java/org/objectweb/asm/test/AsmTest.java +++ b/asm-test/src/main/java/org/objectweb/asm/test/AsmTest.java @@ -152,6 +152,7 @@ public enum PrecompiledClass { JDK8_ALL_FRAMES("jdk8.AllFrames", JdkVersion.JDK8), JDK8_ALL_INSTRUCTIONS("jdk8.AllInstructions", JdkVersion.JDK8), JDK8_ALL_STRUCTURES("jdk8.AllStructures", JdkVersion.JDK8), + JDK8_ALL_LAMBDAS("jdk8.AllLambdas", JdkVersion.JDK8), JDK8_ANONYMOUS_INNER_CLASS("jdk8.AllStructures$1", JdkVersion.JDK8), JDK8_ARTIFICIAL_STRUCTURES("jdk8.Artificial$()$Structures", JdkVersion.JDK8), JDK8_INNER_CLASS("jdk8.AllStructures$InnerClass", JdkVersion.JDK8), diff --git a/asm-test/src/main/resources/jdk8/AllLambdas$InnerClass.class b/asm-test/src/main/resources/jdk8/AllLambdas$InnerClass.class new file mode 100644 index 0000000000000000000000000000000000000000..81c62d84ea08ed3ab9bc61a097f406b1e1fae56b GIT binary patch literal 260 zcmZ8bF%E)25S&8yHdxYb42PK$^cO^rF>R4CF|` kK}Z>3QMG9rHw>}Qrs+|wSHKho96|#w`h;$C=wWC%KQR3^`Tzg` literal 0 HcmV?d00001 diff --git a/asm-test/src/main/resources/jdk8/AllLambdas.class b/asm-test/src/main/resources/jdk8/AllLambdas.class new file mode 100644 index 0000000000000000000000000000000000000000..28a0f8e7f681c1bd618277939950d5e463b7e9cd GIT binary patch literal 2298 zcmcIkTXWk)6#h1jEk{vmN4WrPT9P(}TH3Uw&7<+ic5juLrgWI03m z0sH{o`o=SzVZuu~6K0^#3_pRN!5ag^Sxb)O#FGaGJeG8J_xsMC@9f$0>)(ez1DHXT zA+uxc-pDOFPJvgpEMC*V7_vKjkLMgN%egySJEG)kNDwqaT~Pprv&YDzc`1dr?C=`- zNipcrnBj8d)RuehuE?zmf7`WgacMcCHd=7q-Fj6+7sG|qp&GgwQURWfIa}KP0z+bK z{GNd`$mr-qmcfv&SK*hDMeA?SY&Tbo!8FX!&508iPh?6nkx&l$MlI1qhWh)Gq`&f2NmP|o4JGQ9 zdAUb5Xbn4!7Cs!63|E*mDEBS-4G%BqmSr6lwi!BuL&_9vDK_erE#YmdqGsr6w8nK_ z4aY{*)CHZKUUchTNvzrmn_Y5Uzvg?q8tAHFgl=Cj*Ge2+G17vT7^IeA^#rrh#U$~+y?EeJozWL}G>|ZFx4mkAg zMe;8UX;@-<|DU)`|CSR^ph6(kI5m3GsV?Z0>K?oV*>I7BOWy@5reR82AI()V + L0 + LINENUMBER 32 L0 + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x9 + public static normalLambda()V + L0 + LINENUMBER 39 L0 + INVOKEDYNAMIC run()Ljava/lang/Runnable; [ + // handle kind 0x6 : INVOKESTATIC + java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + // arguments: + ()V, + // handle kind 0x6 : INVOKESTATIC + java/lang/Thread.dumpStack()V, + ()V + ] + ASTORE 0 + L1 + LINENUMBER 40 L1 + RETURN + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x9 + public static advancedLambda()V + L0 + LINENUMBER 43 L0 + INVOKEDYNAMIC run()Ljava/lang/Runnable; [ + // handle kind 0x6 : INVOKESTATIC + java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; + // arguments: + ()V, + // handle kind 0x6 : INVOKESTATIC + java/lang/Thread.dumpStack()V, + ()V, + 6, + 1, + jdk8.AllLambdas$InnerClass.class, + 0 + ] + CHECKCAST jdk8/AllLambdas$InnerClass + CHECKCAST java/lang/Runnable + ASTORE 0 + L1 + LINENUMBER 44 L1 + RETURN + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x9 + public static serializableLambda()V + L0 + LINENUMBER 47 L0 + INVOKEDYNAMIC run()Ljava/lang/Runnable; [ + // handle kind 0x6 : INVOKESTATIC + java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; + // arguments: + ()V, + // handle kind 0x6 : INVOKESTATIC + java/lang/Thread.dumpStack()V, + ()V, + 5, + 0 + ] + CHECKCAST java/io/Serializable + CHECKCAST java/lang/Runnable + ASTORE 0 + L1 + LINENUMBER 48 L1 + RETURN + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x9 + public static serializableAdvancedLambda()V + L0 + LINENUMBER 51 L0 + INVOKEDYNAMIC run()Ljava/lang/Runnable; [ + // handle kind 0x6 : INVOKESTATIC + java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; + // arguments: + ()V, + // handle kind 0x6 : INVOKESTATIC + java/lang/Thread.dumpStack()V, + ()V, + 7, + 1, + jdk8.AllLambdas$InnerClass.class, + 0 + ] + CHECKCAST java/io/Serializable + CHECKCAST jdk8/AllLambdas$InnerClass + CHECKCAST java/lang/Runnable + ASTORE 0 + L1 + LINENUMBER 52 L1 + RETURN + MAXSTACK = 1 + MAXLOCALS = 1 + + // access flags 0x100A + private static synthetic $deserializeLambda$(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object; + L0 + LINENUMBER 32 L0 + ALOAD 0 + INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getImplMethodName ()Ljava/lang/String; + ASTORE 1 + ICONST_M1 + ISTORE 2 + ALOAD 1 + INVOKEVIRTUAL java/lang/String.hashCode ()I + LOOKUPSWITCH + -1366118156: L1 + default: L2 + L1 + FRAME APPEND [java/lang/String I] + ALOAD 1 + LDC "dumpStack" + INVOKEVIRTUAL java/lang/String.equals (Ljava/lang/Object;)Z + IFEQ L2 + ICONST_0 + ISTORE 2 + L2 + FRAME SAME + ILOAD 2 + LOOKUPSWITCH + 0: L3 + default: L4 + L3 + FRAME SAME + ALOAD 0 + INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getImplMethodKind ()I + BIPUSH 6 + IF_ICMPNE L5 + ALOAD 0 + INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getFunctionalInterfaceClass ()Ljava/lang/String; + LDC "java/lang/Runnable" + INVOKEVIRTUAL java/lang/Object.equals (Ljava/lang/Object;)Z + IFEQ L5 + ALOAD 0 + INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getFunctionalInterfaceMethodName ()Ljava/lang/String; + LDC "run" + INVOKEVIRTUAL java/lang/Object.equals (Ljava/lang/Object;)Z + IFEQ L5 + ALOAD 0 + INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getFunctionalInterfaceMethodSignature ()Ljava/lang/String; + LDC "()V" + INVOKEVIRTUAL java/lang/Object.equals (Ljava/lang/Object;)Z + IFEQ L5 + ALOAD 0 + INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getImplClass ()Ljava/lang/String; + LDC "java/lang/Thread" + INVOKEVIRTUAL java/lang/Object.equals (Ljava/lang/Object;)Z + IFEQ L5 + ALOAD 0 + INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getImplMethodSignature ()Ljava/lang/String; + LDC "()V" + INVOKEVIRTUAL java/lang/Object.equals (Ljava/lang/Object;)Z + IFEQ L5 + INVOKEDYNAMIC run()Ljava/lang/Runnable; [ + // handle kind 0x6 : INVOKESTATIC + java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; + // arguments: + ()V, + // handle kind 0x6 : INVOKESTATIC + java/lang/Thread.dumpStack()V, + ()V, + 5, + 0 + ] + ARETURN + L5 + FRAME SAME + ALOAD 0 + INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getImplMethodKind ()I + BIPUSH 6 + IF_ICMPNE L4 + ALOAD 0 + INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getFunctionalInterfaceClass ()Ljava/lang/String; + LDC "java/lang/Runnable" + INVOKEVIRTUAL java/lang/Object.equals (Ljava/lang/Object;)Z + IFEQ L4 + ALOAD 0 + INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getFunctionalInterfaceMethodName ()Ljava/lang/String; + LDC "run" + INVOKEVIRTUAL java/lang/Object.equals (Ljava/lang/Object;)Z + IFEQ L4 + ALOAD 0 + INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getFunctionalInterfaceMethodSignature ()Ljava/lang/String; + LDC "()V" + INVOKEVIRTUAL java/lang/Object.equals (Ljava/lang/Object;)Z + IFEQ L4 + ALOAD 0 + INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getImplClass ()Ljava/lang/String; + LDC "java/lang/Thread" + INVOKEVIRTUAL java/lang/Object.equals (Ljava/lang/Object;)Z + IFEQ L4 + ALOAD 0 + INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getImplMethodSignature ()Ljava/lang/String; + LDC "()V" + INVOKEVIRTUAL java/lang/Object.equals (Ljava/lang/Object;)Z + IFEQ L4 + INVOKEDYNAMIC run()Ljava/lang/Runnable; [ + // handle kind 0x6 : INVOKESTATIC + java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; + // arguments: + ()V, + // handle kind 0x6 : INVOKESTATIC + java/lang/Thread.dumpStack()V, + ()V, + 7, + 1, + jdk8.AllLambdas$InnerClass.class, + 0 + ] + ARETURN + L4 + FRAME SAME + NEW java/lang/IllegalArgumentException + DUP + LDC "Invalid lambda deserialization" + INVOKESPECIAL java/lang/IllegalArgumentException. (Ljava/lang/String;)V + ATHROW + MAXSTACK = 3 + MAXLOCALS = 3 +} diff --git a/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java b/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java index 10d39b4c..8e483462 100644 --- a/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java +++ b/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java @@ -351,9 +351,10 @@ void testAccept_emptyVisitor_skipCode( // jdk8.ArtificialStructures contains structures which require ASM5, but only inside the method // code. Here we skip the code, so this class can be read with ASM4. Likewise for - // jdk11.AllInstructions. + // jdk8.AllLambdas and jdk11.AllInstructions. if (classParameter.isMoreRecentThan(apiParameter) && classParameter != PrecompiledClass.JDK8_ARTIFICIAL_STRUCTURES + && classParameter != PrecompiledClass.JDK8_ALL_LAMBDAS && classParameter != PrecompiledClass.JDK11_ALL_INSTRUCTIONS) { Exception exception = assertThrows(UnsupportedOperationException.class, accept); assertTrue(exception.getMessage().matches(UNSUPPORTED_OPERATION_MESSAGE_PATTERN)); diff --git a/build.gradle b/build.gradle index bfa9d0ff..cc6c65d1 100644 --- a/build.gradle +++ b/build.gradle @@ -101,8 +101,8 @@ project(':asm-commons') { project(':asm-test') { description = "Utilities for testing ${parent.description}" provides = ['org.objectweb.asm.test'] - depends = ['org.junit.jupiter:junit-jupiter-api:5.10.1', - 'org.junit.jupiter:junit-jupiter-params:5.10.1'] + depends = ['org.junit.jupiter:junit-jupiter-api:5.13.4', + 'org.junit.jupiter:junit-jupiter-params:5.13.4'] } project(':asm-tree') { @@ -211,9 +211,10 @@ subprojects { dependencies { requires.each { projectName -> api project(projectName) } depends.each { artifactName -> api artifactName } - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.1', - 'org.junit.jupiter:junit-jupiter-params:5.10.1' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.1' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.13.4', + 'org.junit.jupiter:junit-jupiter-params:5.13.4' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.13.4' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testImplementation project(':asm-test') } From c911d6da74ecc123ab6b1b350360d0bbf219f56f Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sat, 4 Oct 2025 16:02:13 +0200 Subject: [PATCH 10/23] Update ASM version. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index cc6c65d1..c8f1b8ab 100644 --- a/build.gradle +++ b/build.gradle @@ -47,7 +47,7 @@ dependencies { allprojects { group = 'org.ow2.asm' - version = '9.9' + (rootProject.hasProperty('release') ? '' : '-SNAPSHOT') + version = '9.10' + (rootProject.hasProperty('release') ? '' : '-SNAPSHOT') } subprojects { From 124a45002ba09a6bf6fc6ce4a428321737f466f1 Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Sun, 19 Oct 2025 11:10:17 +0000 Subject: [PATCH 11/23] Support remapping lambda --- .../org/objectweb/asm/commons/Remapper.java | 108 ++++++- .../objectweb/asm/commons/SimpleRemapper.java | 2 +- .../asm/commons/ClassRemapperTest.java | 6 +- .../asm/commons/LambdaRemapTest.java | 286 ++++++++++++++++++ 4 files changed, 397 insertions(+), 5 deletions(-) create mode 100644 asm-commons/src/test/java/org/objectweb/asm/commons/LambdaRemapTest.java diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java index f619750e..047eb2a7 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java @@ -43,6 +43,20 @@ */ public abstract class Remapper { + // The class name of LambdaMetafactory. + private static final String LAMBDA_FACTORY_CLASSNAME = "java/lang/invoke/LambdaMetafactory"; + + // The method signature of LambdaMetafactory.metafactory(...). + private static final String LAMBDA_FACTORY_METAFACTORY = + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;" + + "Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;" + + "Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; + + // The method signature of LambdaMetafactory.altMetafactory(...). + private static final String LAMBDA_FACTORY_ALTMETAFACTORY = + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;" + + "[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;"; + /** * The ASM API version supported by this remapper, or 0 for instances created with the deprecated * constructor. @@ -367,6 +381,98 @@ public String mapInvokeDynamicMethodName(final String name, final String descrip return name; } + /** + * Maps an invokedynamic or a constant dynamic method name to its new name. Subclasses can + * override. + * + *

    The default implementation of this method first performs well-known rule checks (calling + * {@link #mapWellKnownInvokeDynamicMethodName(String, String, Handle, Object...)}) and then + * performs basic remapping (calling {@link #mapBasicInvokeDynamicMethodName(String, String, + * Handle, Object...)}). + * + *

    For most users, only {@link #mapBasicInvokeDynamicMethodName(String, String, Handle, + * Object...)} needs to be overridden. + * + * @param name the name of the method. + * @param descriptor the descriptor of the method. + * @param bootstrapMethodHandle the bootstrap method. + * @param bootstrapMethodArguments the bootstrap method constant arguments. Each argument must be + * an {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link + * Type}, {@link Handle} or {@link ConstantDynamic} value. This method is allowed to modify + * the content of the array so a caller should expect that this array may change. + * @return the new name of the method. + */ + public String mapInvokeDynamicMethodName( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + String mappedWellKnownName = + mapWellKnownInvokeDynamicMethodName( + name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); + if (mappedWellKnownName != null) { + return mappedWellKnownName; + } + return mapBasicInvokeDynamicMethodName( + name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); + } + + /** + * Maps well-known invokedynamic (e.g. lambda creation) or const dynamic method names to their new + * names. This method detects specific invokedynamic method rules and remaps using the + * corresponding rules. When no rule is matched, returns {@literal null}. When non-null is + * returned, it means that this invokedynamic method name matches a rule and has been remapped + * with the relevant rule. Subclasses can override. + * + * @param name the name of the method. + * @param descriptor the descriptor of the method. + * @param bootstrapMethodHandle the bootstrap method. + * @param bootstrapMethodArguments the bootstrap method constant arguments. Each argument must be + * an {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link + * Type}, {@link Handle} or {@link ConstantDynamic} value. This method is allowed to modify + * the content of the array so a caller should expect that this array may change. + * @return the new name of the method, or null if no special rule is matched. + */ + public String mapWellKnownInvokeDynamicMethodName( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + + if (LAMBDA_FACTORY_CLASSNAME.equals(bootstrapMethodHandle.getOwner()) + && bootstrapMethodHandle.getTag() == Opcodes.H_INVOKESTATIC) { + // This is a lambda creation. + // Note: **if** is reserved for future JDK changes. + boolean isMetafactory = false; + isMetafactory |= + "metafactory".equals(bootstrapMethodHandle.getName()) + && LAMBDA_FACTORY_METAFACTORY.equals(bootstrapMethodHandle.getDesc()); + isMetafactory |= + "altMetafactory".equals(bootstrapMethodHandle.getName()) + && LAMBDA_FACTORY_ALTMETAFACTORY.equals(bootstrapMethodHandle.getDesc()); + + if (isMetafactory) { + // Note: + // Java lambda instances are created by LambdaMetafactory.metafactory() and + // LambdaMetafactory.altMetafactory(). + // The specification can be found in the LambdaMetafactory javadoc: + // https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/invoke/LambdaMetafactory.html + // + // In short, all the necessary parameters can be obtained from this invokedynamic, including + // the following three: + // - Class name: From return type of method descriptor. + // - Method name: Same as the name of invokedynamic. + // - Method descriptor: From the first bootstrap argument. + return mapMethodName( + Type.getReturnType(descriptor).getInternalName(), + name, + bootstrapMethodArguments[0].toString()); + } + } + + return null; + } + /** * Maps an invokedynamic or a constant dynamic method name to its new name. The default * implementation of this method returns the given name, unchanged. Subclasses can override. @@ -380,7 +486,7 @@ public String mapInvokeDynamicMethodName(final String name, final String descrip * the content of the array so a caller should expect that this array may change. * @return the new name of the method. */ - public String mapInvokeDynamicMethodName( + public String mapBasicInvokeDynamicMethodName( final String name, final String descriptor, final Handle bootstrapMethodHandle, diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java index fdae678c..a700feb8 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java @@ -136,7 +136,7 @@ public String mapInvokeDynamicMethodName(final String name, final String descrip } @Override - public String mapInvokeDynamicMethodName( + public String mapBasicInvokeDynamicMethodName( final String name, final String descriptor, final Handle bootstrapMethodHandle, diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/ClassRemapperTest.java b/asm-commons/src/test/java/org/objectweb/asm/commons/ClassRemapperTest.java index 2774cca4..75616189 100644 --- a/asm-commons/src/test/java/org/objectweb/asm/commons/ClassRemapperTest.java +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/ClassRemapperTest.java @@ -252,7 +252,7 @@ void testVisitLdcInsn_constantDynamic() { classNode, new Remapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL) { @Override - public String mapInvokeDynamicMethodName( + public String mapBasicInvokeDynamicMethodName( final String name, final String descriptor, final Handle bootstrapMethodHandle, @@ -335,7 +335,7 @@ void testInvokeDynamicInsn_field() { classNode, new Remapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL) { @Override - public String mapInvokeDynamicMethodName( + public String mapBasicInvokeDynamicMethodName( final String name, final String descriptor, final Handle bootstrapMethodHandle, @@ -542,7 +542,7 @@ public String mapMethodName(final String owner, final String name, final String } @Override - public String mapInvokeDynamicMethodName( + public String mapBasicInvokeDynamicMethodName( final String name, final String descriptor, final Handle bootstrapMethodHandle, diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/LambdaRemapTest.java b/asm-commons/src/test/java/org/objectweb/asm/commons/LambdaRemapTest.java new file mode 100644 index 00000000..ed017e62 --- /dev/null +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/LambdaRemapTest.java @@ -0,0 +1,286 @@ +package org.objectweb.asm.commons; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Map; +import java.util.stream.Stream; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.Parameter; +import org.junit.jupiter.params.ParameterizedClass; +import org.junit.jupiter.params.provider.MethodSource; +import org.objectweb.asm.Handle; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.test.AsmTest; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; + +@SuppressWarnings("UnnecessaryBoxing") +class LambdaRemapTest extends AsmTest implements Opcodes { + + // The method signature of LambdaMetafactory.metafactory(...). + private static final String LAMBDA_FACTORY_METAFACTORY = + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;" + + "Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;" + + "Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; + + // The method signature of LambdaMetafactory.altMetafactory(...). + private static final String LAMBDA_FACTORY_ALTMETAFACTORY = + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;" + + "[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;"; + + /* + public class LambdaTest { + public interface TestInterface { + default void helloWorld() { + } + } + + public static void normalLambda() { + Runnable runnable = Thread::dumpStack; + } + + public static void serializableLambda() { + Runnable runnable = (Runnable & Serializable) Thread::dumpStack; + } + + public static void advancedLambda() { + Runnable runnable = (Runnable & Serializable & TestInterface) Thread::dumpStack; + } + } + */ + void func_normalLambda(final MethodVisitor methodVisitor) { + methodVisitor.visitInvokeDynamicInsn( + "run", + "()Ljava/lang/Runnable;", + new Handle( + Opcodes.H_INVOKESTATIC, + "java/lang/invoke/LambdaMetafactory", + "metafactory", + LAMBDA_FACTORY_METAFACTORY, + false), + new Object[] { + Type.getType("()V"), + new Handle(Opcodes.H_INVOKESTATIC, "java/lang/Thread", "dumpStack", "()V", false), + Type.getType("()V") + }); + } + + void func_serializableLambda(final MethodVisitor methodVisitor) { + methodVisitor.visitInvokeDynamicInsn( + "run", + "()Ljava/lang/Runnable;", + new Handle( + Opcodes.H_INVOKESTATIC, + "java/lang/invoke/LambdaMetafactory", + "altMetafactory", + LAMBDA_FACTORY_ALTMETAFACTORY, + false), + new Object[] { + Type.getType("()V"), + new Handle(Opcodes.H_INVOKESTATIC, "java/lang/Thread", "dumpStack", "()V", false), + Type.getType("()V"), + Integer.valueOf(5), + Integer.valueOf(0) + }); + } + + void func_advancedLambda(final MethodVisitor methodVisitor) { + methodVisitor.visitInvokeDynamicInsn( + "run", + "()Ljava/lang/Runnable;", + new Handle( + Opcodes.H_INVOKESTATIC, + "java/lang/invoke/LambdaMetafactory", + "altMetafactory", + LAMBDA_FACTORY_ALTMETAFACTORY, + false), + new Object[] { + Type.getType("()V"), + new Handle(Opcodes.H_INVOKESTATIC, "java/lang/Thread", "dumpStack", "()V", false), + Type.getType("()V"), + Integer.valueOf(7), + Integer.valueOf(1), + Type.getType("Lpkg/LambdaTest$TestInterface;"), + Integer.valueOf(0) + }); + } + + public static Stream remappersLatest() { + return Stream.of( + new Remapper(/* latest */ Opcodes.ASM10_EXPERIMENTAL) { + @Override + public String mapMethodName( + final String owner, final String name, final String descriptor) { + if ("java/lang/Runnable".equals(owner) && "run".equals(name)) { + return "call"; + } + return name; + } + + @Override + public String map(final String internalName) { + if ("java/lang/Runnable".equals(internalName)) { + return "me/MyRunnable"; + } + return super.map(internalName); + } + }, + new SimpleRemapper( + /* latest */ Opcodes.ASM10_EXPERIMENTAL, + Map.of( + "java/lang/Runnable", "me/MyRunnable", + "java/lang/Runnable.run()V", "call"))); + } + + public static Stream remappersDeprecated() { + return Stream.of( + new Remapper() { + @Override + public String mapMethodName( + final String owner, final String name, final String descriptor) { + if ("java/lang/Runnable".equals(owner) && "run".equals(name)) { + return "call"; + } + return name; + } + + @Override + public String map(final String internalName) { + if ("java/lang/Runnable".equals(internalName)) { + return "me/MyRunnable"; + } + return super.map(internalName); + } + }, + new SimpleRemapper( + Map.of( + "java/lang/Runnable", "me/MyRunnable", + "java/lang/Runnable.run()V", "call"))); + } + + @Nested + @ParameterizedClass + @MethodSource("org.objectweb.asm.commons.LambdaRemapTest#remappersLatest") + class LatestApi { + + @Parameter Remapper remapper; + + @Test + void normalLambda() { + ClassNode classNode = new ClassNode(); + ClassRemapper classRemapper = + new ClassRemapper(/* latest api */ Opcodes.ASM9, classNode, remapper); + classRemapper.visit(Opcodes.V11, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null); + MethodVisitor methodVisitor = + classRemapper.visitMethod(Opcodes.ACC_PUBLIC, "hello", "()V", null, null); + methodVisitor.visitCode(); + + func_normalLambda(methodVisitor); + + InvokeDynamicInsnNode invokeDynamic = + (InvokeDynamicInsnNode) classNode.methods.get(0).instructions.get(0); + assertEquals("()Lme/MyRunnable;", invokeDynamic.desc); + assertEquals("call", invokeDynamic.name); + } + + @Test + void serializableLambda() { + ClassNode classNode = new ClassNode(); + ClassRemapper classRemapper = + new ClassRemapper(/* latest api */ Opcodes.ASM9, classNode, remapper); + classRemapper.visit(Opcodes.V11, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null); + MethodVisitor methodVisitor = + classRemapper.visitMethod(Opcodes.ACC_PUBLIC, "hello", "()V", null, null); + methodVisitor.visitCode(); + + func_serializableLambda(methodVisitor); + + InvokeDynamicInsnNode invokeDynamic = + (InvokeDynamicInsnNode) classNode.methods.get(0).instructions.get(0); + assertEquals("()Lme/MyRunnable;", invokeDynamic.desc); + assertEquals("call", invokeDynamic.name); + } + + @Test + void advancedLambda() { + ClassNode classNode = new ClassNode(); + ClassRemapper classRemapper = + new ClassRemapper(/* latest api */ Opcodes.ASM9, classNode, remapper); + classRemapper.visit(Opcodes.V11, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null); + MethodVisitor methodVisitor = + classRemapper.visitMethod(Opcodes.ACC_PUBLIC, "hello", "()V", null, null); + methodVisitor.visitCode(); + + func_advancedLambda(methodVisitor); + + InvokeDynamicInsnNode invokeDynamic = + (InvokeDynamicInsnNode) classNode.methods.get(0).instructions.get(0); + assertEquals("()Lme/MyRunnable;", invokeDynamic.desc); + assertEquals("call", invokeDynamic.name); + } + } + + @Nested + @ParameterizedClass + @MethodSource("org.objectweb.asm.commons.LambdaRemapTest#remappersDeprecated") + class DeprecatedApi { + @Parameter Remapper remapper; + + @Test + void normalLambda() { + ClassNode classNode = new ClassNode(); + ClassRemapper classRemapper = + new ClassRemapper(/* latest api */ Opcodes.ASM9, classNode, remapper); + classRemapper.visit(Opcodes.V11, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null); + MethodVisitor methodVisitor = + classRemapper.visitMethod(Opcodes.ACC_PUBLIC, "hello", "()V", null, null); + methodVisitor.visitCode(); + + func_normalLambda(methodVisitor); + + InvokeDynamicInsnNode invokeDynamic = + (InvokeDynamicInsnNode) classNode.methods.get(0).instructions.get(0); + assertEquals("()Lme/MyRunnable;", invokeDynamic.desc); + assertEquals("run", invokeDynamic.name); + } + + @Test + void serializableLambda() { + ClassNode classNode = new ClassNode(); + ClassRemapper classRemapper = + new ClassRemapper(/* latest api */ Opcodes.ASM9, classNode, remapper); + classRemapper.visit(Opcodes.V11, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null); + MethodVisitor methodVisitor = + classRemapper.visitMethod(Opcodes.ACC_PUBLIC, "hello", "()V", null, null); + methodVisitor.visitCode(); + + func_serializableLambda(methodVisitor); + + InvokeDynamicInsnNode invokeDynamic = + (InvokeDynamicInsnNode) classNode.methods.get(0).instructions.get(0); + assertEquals("()Lme/MyRunnable;", invokeDynamic.desc); + assertEquals("run", invokeDynamic.name); + } + + @Test + void advancedLambda() { + ClassNode classNode = new ClassNode(); + ClassRemapper classRemapper = + new ClassRemapper(/* latest api */ Opcodes.ASM9, classNode, remapper); + classRemapper.visit(Opcodes.V11, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null); + MethodVisitor methodVisitor = + classRemapper.visitMethod(Opcodes.ACC_PUBLIC, "hello", "()V", null, null); + methodVisitor.visitCode(); + + func_advancedLambda(methodVisitor); + + InvokeDynamicInsnNode invokeDynamic = + (InvokeDynamicInsnNode) classNode.methods.get(0).instructions.get(0); + assertEquals("()Lme/MyRunnable;", invokeDynamic.desc); + assertEquals("run", invokeDynamic.name); + } + } +} From 142cd7912eba3805e7e18f6176e50f0d8b9e107a Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sun, 19 Oct 2025 15:54:18 +0200 Subject: [PATCH 12/23] Include only the Java files for Sonar analysis, as a workaround for an issue with the web linter. --- build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c8f1b8ab..6716b32e 100644 --- a/build.gradle +++ b/build.gradle @@ -33,7 +33,7 @@ buildscript { plugins { id 'com.diffplug.spotless' version "6.23.3" apply false } plugins { id 'me.champeau.jmh' version '0.7.2' apply false } -plugins { id 'org.sonarqube' version '6.2.0.5505' apply false } +plugins { id 'org.sonarqube' version '7.0.0.6105' apply false } description = 'ASM, a very small and fast Java bytecode manipulation framework' @@ -332,6 +332,7 @@ configure(subprojects.findAll{it.provides}) { apply plugin: 'org.sonarqube' sonar { properties { property 'sonar.projectKey', "ASM:${project.name}" } + properties { property 'sonar.inclusions', "**/*.java" } } // Add a task to generate a private javadoc and add it as a dependency of the From 829f9424915fefdb4b7e8284d3313e863df01034 Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sun, 26 Oct 2025 11:12:10 +0100 Subject: [PATCH 13/23] Tweak SonarQube configuration. --- .gitlab-ci.yml | 2 +- build.gradle | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5d7242b1..7abdd572 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,4 +32,4 @@ sonar-job: stage: deploy allow_failure: true script: - - if [ $SONAR_LOGIN ]; then $GRADLE -Dorg.gradle.jvmargs='-XX:MetaspaceSize=1024M -XX:MaxMetaspaceSize=1024M' jacocoTestReport sonar -Dsonar.host.url=$SONAR -Dsonar.login=${SONAR_LOGIN}; fi + - if [ $SONAR_LOGIN ]; then $GRADLE -Dorg.gradle.jvmargs='-XX:MetaspaceSize=1024M -XX:MaxMetaspaceSize=1024M' jacocoTestReport sonar -Dsonar.host.url=$SONAR -Dsonar.token=${SONAR_LOGIN}; fi diff --git a/build.gradle b/build.gradle index 6716b32e..248068f2 100644 --- a/build.gradle +++ b/build.gradle @@ -333,6 +333,7 @@ configure(subprojects.findAll{it.provides}) { sonar { properties { property 'sonar.projectKey', "ASM:${project.name}" } properties { property 'sonar.inclusions', "**/*.java" } + properties { property 'sonar.verbose', "true" } } // Add a task to generate a private javadoc and add it as a dependency of the From bcbb529a0f8d93938fef86320aae9317301501c9 Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sun, 26 Oct 2025 11:25:44 +0100 Subject: [PATCH 14/23] Enable Gradle logs for the Sonar job. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7abdd572..1eed5035 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,4 +32,4 @@ sonar-job: stage: deploy allow_failure: true script: - - if [ $SONAR_LOGIN ]; then $GRADLE -Dorg.gradle.jvmargs='-XX:MetaspaceSize=1024M -XX:MaxMetaspaceSize=1024M' jacocoTestReport sonar -Dsonar.host.url=$SONAR -Dsonar.token=${SONAR_LOGIN}; fi + - if [ $SONAR_LOGIN ]; then $GRADLE --info -Dorg.gradle.jvmargs='-XX:MetaspaceSize=1024M -XX:MaxMetaspaceSize=1024M' jacocoTestCoverageVerification jacocoTestReport sonar -Dsonar.host.url=$SONAR -Dsonar.token=${SONAR_LOGIN}; fi From 26131536e6d919d8ea0f53f438ef99665437a8e7 Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sun, 26 Oct 2025 11:56:23 +0100 Subject: [PATCH 15/23] Test using artifacts to share outputs between stages. --- .gitlab-ci.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1eed5035..396ec20a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,11 +16,17 @@ build-job: stage: build script: - $GRADLE build + artifacts: + untracked: true + expire_in: 1 hour test-job: stage: test script: - - $GRADLE test jacocoTestCoverageVerification + - $GRADLE test jacocoTestCoverageVerification jacocoTestReport + artifacts: + untracked: true + expire_in: 1 hour deploy-job: stage: deploy @@ -32,4 +38,4 @@ sonar-job: stage: deploy allow_failure: true script: - - if [ $SONAR_LOGIN ]; then $GRADLE --info -Dorg.gradle.jvmargs='-XX:MetaspaceSize=1024M -XX:MaxMetaspaceSize=1024M' jacocoTestCoverageVerification jacocoTestReport sonar -Dsonar.host.url=$SONAR -Dsonar.token=${SONAR_LOGIN}; fi + - if [ $SONAR_LOGIN ]; then $GRADLE --info -Dorg.gradle.jvmargs='-XX:MetaspaceSize=1024M -XX:MaxMetaspaceSize=1024M' sonar -Dsonar.host.url=$SONAR -Dsonar.token=${SONAR_LOGIN}; fi From db1cf8d0af6c92c63f6da8b5e30f1aa2380414fa Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sun, 26 Oct 2025 12:47:51 +0100 Subject: [PATCH 16/23] Attempt to fix Gradle CI build error. --- .gitlab-ci.yml | 2 +- build.gradle | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 396ec20a..9de00d7b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,7 +23,7 @@ build-job: test-job: stage: test script: - - $GRADLE test jacocoTestCoverageVerification jacocoTestReport + - $GRADLE --info test jacocoTestCoverageVerification jacocoTestReport artifacts: untracked: true expire_in: 1 hour diff --git a/build.gradle b/build.gradle index 248068f2..53031a0c 100644 --- a/build.gradle +++ b/build.gradle @@ -236,6 +236,7 @@ configure(subprojects.findAll{it.provides}) { jacocoTestReport { reports { xml.required = true } classDirectories.setFrom(sourceSets.main.output.classesDirs) + dependsOn ':asm:jar' } jacocoTestCoverageVerification { classDirectories.setFrom(sourceSets.main.output.classesDirs) From 9a92389f8832c7f32424bbc5909ee713a4ca3425 Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sun, 26 Oct 2025 12:55:16 +0100 Subject: [PATCH 17/23] New attempt to fix Gradle CI build error. --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index 53031a0c..b1a59f4e 100644 --- a/build.gradle +++ b/build.gradle @@ -237,6 +237,8 @@ configure(subprojects.findAll{it.provides}) { reports { xml.required = true } classDirectories.setFrom(sourceSets.main.output.classesDirs) dependsOn ':asm:jar' + dependsOn ':asm-tree:jar' + dependsOn ':asm-commons:jar' } jacocoTestCoverageVerification { classDirectories.setFrom(sourceSets.main.output.classesDirs) From 176f6160eb7f68e2b2632194cc56baa309ec8f62 Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sun, 26 Oct 2025 14:49:17 +0100 Subject: [PATCH 18/23] Add forRemoval=false in @Deprecated annotations. Also fix other issues found with SonarQube. --- .../java/org/objectweb/asm/commons/AnnotationRemapper.java | 6 +++--- .../main/java/org/objectweb/asm/commons/ClassRemapper.java | 2 +- .../main/java/org/objectweb/asm/commons/FieldRemapper.java | 2 +- .../java/org/objectweb/asm/commons/InstructionAdapter.java | 6 +++--- .../main/java/org/objectweb/asm/commons/MethodRemapper.java | 2 +- .../org/objectweb/asm/commons/RecordComponentRemapper.java | 2 +- .../src/main/java/org/objectweb/asm/commons/Remapper.java | 6 +++--- .../main/java/org/objectweb/asm/commons/SimpleRemapper.java | 4 ++-- asm-util/src/main/java/org/objectweb/asm/util/Printer.java | 2 +- .../src/main/java/org/objectweb/asm/util/Textifier.java | 2 +- asm/src/main/java/org/objectweb/asm/Attribute.java | 2 +- asm/src/main/java/org/objectweb/asm/ClassReader.java | 2 +- asm/src/main/java/org/objectweb/asm/ClassWriter.java | 2 +- asm/src/main/java/org/objectweb/asm/Handle.java | 2 +- asm/src/main/java/org/objectweb/asm/MethodVisitor.java | 2 +- asm/src/main/java/org/objectweb/asm/Opcodes.java | 3 ++- asm/src/test/java/org/objectweb/asm/ClassWriterTest.java | 2 +- asm/src/test/java/org/objectweb/asm/ConstantsTest.java | 2 +- asm/src/test/java/org/objectweb/asm/MethodVisitorTest.java | 2 +- .../org/objectweb/asm/signature/SignaturesProviders.java | 2 +- .../jmh/java/org/objectweb/asm/benchmarks/AsmAdapter.java | 2 +- 21 files changed, 29 insertions(+), 28 deletions(-) diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/AnnotationRemapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/AnnotationRemapper.java index fa41dc69..15bb8d07 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/AnnotationRemapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/AnnotationRemapper.java @@ -55,7 +55,7 @@ public class AnnotationRemapper extends AnnotationVisitor { * @param remapper the remapper to use to remap the types in the visited annotation. * @deprecated use {@link #AnnotationRemapper(String, AnnotationVisitor, Remapper)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) public AnnotationRemapper(final AnnotationVisitor annotationVisitor, final Remapper remapper) { this(/* descriptor= */ null, annotationVisitor, remapper); } @@ -83,7 +83,7 @@ public AnnotationRemapper( * @param remapper the remapper to use to remap the types in the visited annotation. * @deprecated use {@link #AnnotationRemapper(int, String, AnnotationVisitor, Remapper)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) protected AnnotationRemapper( final int api, final AnnotationVisitor annotationVisitor, final Remapper remapper) { this(api, /* descriptor= */ null, annotationVisitor, remapper); @@ -151,7 +151,7 @@ public AnnotationVisitor visitArray(final String name) { * @return the newly created remapper. * @deprecated use {@link #createAnnotationRemapper(String, AnnotationVisitor)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) { return new AnnotationRemapper(api, /* descriptor= */ null, annotationVisitor, remapper); } diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/ClassRemapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/ClassRemapper.java index baea2c81..04904afd 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/ClassRemapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/ClassRemapper.java @@ -256,7 +256,7 @@ protected MethodVisitor createMethodRemapper(final MethodVisitor methodVisitor) * @return the newly created remapper. * @deprecated use {@link #createAnnotationRemapper(String, AnnotationVisitor)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) { return new AnnotationRemapper(api, /* descriptor= */ null, annotationVisitor, remapper); } diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/FieldRemapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/FieldRemapper.java index e65650c7..28338c52 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/FieldRemapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/FieldRemapper.java @@ -94,7 +94,7 @@ public AnnotationVisitor visitTypeAnnotation( * @return the newly created remapper. * @deprecated use {@link #createAnnotationRemapper(String, AnnotationVisitor)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) { return new AnnotationRemapper(api, /* descriptor= */ null, annotationVisitor, remapper); } diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/InstructionAdapter.java b/asm-commons/src/main/java/org/objectweb/asm/commons/InstructionAdapter.java index 84f9579a..20ca37e1 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/InstructionAdapter.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/InstructionAdapter.java @@ -1072,7 +1072,7 @@ public void putfield(final String owner, final String name, final String descrip * @param descriptor the method's descriptor (see {@link Type}). * @deprecated use {@link #invokevirtual(String, String, String, boolean)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) public void invokevirtual(final String owner, final String name, final String descriptor) { if (api >= Opcodes.ASM5) { invokevirtual(owner, name, descriptor, false); @@ -1111,7 +1111,7 @@ public void invokevirtual( * @param descriptor the method's descriptor (see {@link Type}). * @deprecated use {@link #invokespecial(String, String, String, boolean)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) public void invokespecial(final String owner, final String name, final String descriptor) { if (api >= Opcodes.ASM5) { invokespecial(owner, name, descriptor, false); @@ -1150,7 +1150,7 @@ public void invokespecial( * @param descriptor the method's descriptor (see {@link Type}). * @deprecated use {@link #invokestatic(String, String, String, boolean)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) public void invokestatic(final String owner, final String name, final String descriptor) { if (api >= Opcodes.ASM5) { invokestatic(owner, name, descriptor, false); diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/MethodRemapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/MethodRemapper.java index 56c35be5..f2a683f0 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/MethodRemapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/MethodRemapper.java @@ -278,7 +278,7 @@ public AnnotationVisitor visitLocalVariableAnnotation( * @return the newly created remapper. * @deprecated use {@link #createAnnotationRemapper(String, AnnotationVisitor)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) { return new AnnotationRemapper(api, /* descriptor= */ null, annotationVisitor, remapper); } diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/RecordComponentRemapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/RecordComponentRemapper.java index 0fa5055e..e7ef210d 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/RecordComponentRemapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/RecordComponentRemapper.java @@ -97,7 +97,7 @@ public AnnotationVisitor visitTypeAnnotation( * @return the newly created remapper. * @deprecated use {@link #createAnnotationRemapper(String, AnnotationVisitor)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) protected AnnotationVisitor createAnnotationRemapper(final AnnotationVisitor annotationVisitor) { return new AnnotationRemapper(api, /* descriptor= */ null, annotationVisitor, remapper); } diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java index 047eb2a7..e17462f3 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java @@ -68,7 +68,7 @@ public abstract class Remapper { * * @deprecated use {@link #Remapper(int)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) public Remapper() { this.api = 0; } @@ -283,7 +283,7 @@ public String mapSignature(final String signature, final boolean typeSignature) * @return the newly created remapper. * @deprecated use {@link #createSignatureRemapper} instead. */ - @Deprecated + @Deprecated(forRemoval = false) protected SignatureVisitor createRemappingSignatureAdapter( final SignatureVisitor signatureVisitor) { return createSignatureRemapper(signatureVisitor); @@ -376,7 +376,7 @@ public String mapMethodName(final String owner, final String name, final String * @return the new name of the method. * @deprecated use {@link #mapInvokeDynamicMethodName(String, String, Handle, Object...)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) public String mapInvokeDynamicMethodName(final String name, final String descriptor) { return name; } diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java index a700feb8..f2df3cbb 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java @@ -63,7 +63,7 @@ public class SimpleRemapper extends Remapper { * * @deprecated use {@link #SimpleRemapper(int, Map)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) public SimpleRemapper(final Map mapping) { this.mapping = mapping; } @@ -103,7 +103,7 @@ public SimpleRemapper(final int api, final Map mapping) { * org.objectweb.asm.Type#getInternalName()}). * @deprecated use {@link #SimpleRemapper(int, String, String)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) public SimpleRemapper(final String oldName, final String newName) { this.mapping = Collections.singletonMap(oldName, newName); } diff --git a/asm-util/src/main/java/org/objectweb/asm/util/Printer.java b/asm-util/src/main/java/org/objectweb/asm/util/Printer.java index f670dcc3..35911c45 100644 --- a/asm-util/src/main/java/org/objectweb/asm/util/Printer.java +++ b/asm-util/src/main/java/org/objectweb/asm/util/Printer.java @@ -953,7 +953,7 @@ public abstract void visitFrame( * @param descriptor the method's descriptor (see {@link Type}). * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) public void visitMethodInsn( final int opcode, final String owner, final String name, final String descriptor) { // This method was abstract before ASM5, and was therefore always overridden (without any diff --git a/asm-util/src/main/java/org/objectweb/asm/util/Textifier.java b/asm-util/src/main/java/org/objectweb/asm/util/Textifier.java index 4df14f1f..b8dcb072 100644 --- a/asm-util/src/main/java/org/objectweb/asm/util/Textifier.java +++ b/asm-util/src/main/java/org/objectweb/asm/util/Textifier.java @@ -1443,7 +1443,7 @@ protected void appendLabel(final Label label) { stringBuilder.append(name); } - @Deprecated + @Deprecated(forRemoval = false) protected void appendHandle(final Handle handle) { appendHandle(handle, tab3); } diff --git a/asm/src/main/java/org/objectweb/asm/Attribute.java b/asm/src/main/java/org/objectweb/asm/Attribute.java index aa34a57c..6fe2f425 100644 --- a/asm/src/main/java/org/objectweb/asm/Attribute.java +++ b/asm/src/main/java/org/objectweb/asm/Attribute.java @@ -95,7 +95,7 @@ public boolean isCodeAttribute() { * a Code attribute that contains labels. * @deprecated no longer used by ASM. */ - @Deprecated + @Deprecated(forRemoval = false) protected Label[] getLabels() { return new Label[0]; } diff --git a/asm/src/main/java/org/objectweb/asm/ClassReader.java b/asm/src/main/java/org/objectweb/asm/ClassReader.java index a807c047..ca5d8b2e 100644 --- a/asm/src/main/java/org/objectweb/asm/ClassReader.java +++ b/asm/src/main/java/org/objectweb/asm/ClassReader.java @@ -100,7 +100,7 @@ public class ClassReader { * @deprecated Use {@link #readByte(int)} and the other read methods instead. This field will * eventually be deleted. */ - @Deprecated + @Deprecated(forRemoval = false) // DontCheck(MemberName): can't be renamed (for backward binary compatibility). public final byte[] b; diff --git a/asm/src/main/java/org/objectweb/asm/ClassWriter.java b/asm/src/main/java/org/objectweb/asm/ClassWriter.java index eeff8130..73a16ae6 100644 --- a/asm/src/main/java/org/objectweb/asm/ClassWriter.java +++ b/asm/src/main/java/org/objectweb/asm/ClassWriter.java @@ -897,7 +897,7 @@ public int newPackage(final String packageName) { * @deprecated this method is superseded by {@link #newHandle(int, String, String, String, * boolean)}. */ - @Deprecated + @Deprecated(forRemoval = false) public int newHandle( final int tag, final String owner, final String name, final String descriptor) { return newHandle(tag, owner, name, descriptor, tag == Opcodes.H_INVOKEINTERFACE); diff --git a/asm/src/main/java/org/objectweb/asm/Handle.java b/asm/src/main/java/org/objectweb/asm/Handle.java index 6ffae75b..b7dbf297 100644 --- a/asm/src/main/java/org/objectweb/asm/Handle.java +++ b/asm/src/main/java/org/objectweb/asm/Handle.java @@ -71,7 +71,7 @@ public final class Handle { * @deprecated this constructor has been superseded by {@link #Handle(int, String, String, String, * boolean)}. */ - @Deprecated + @Deprecated(forRemoval = false) public Handle(final int tag, final String owner, final String name, final String descriptor) { this(tag, owner, name, descriptor, tag == Opcodes.H_INVOKEINTERFACE); } diff --git a/asm/src/main/java/org/objectweb/asm/MethodVisitor.java b/asm/src/main/java/org/objectweb/asm/MethodVisitor.java index 751bc7f5..3d38dcfe 100644 --- a/asm/src/main/java/org/objectweb/asm/MethodVisitor.java +++ b/asm/src/main/java/org/objectweb/asm/MethodVisitor.java @@ -416,7 +416,7 @@ public void visitFieldInsn( * @param descriptor the method's descriptor (see {@link Type}). * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead. */ - @Deprecated + @Deprecated(forRemoval = false) public void visitMethodInsn( final int opcode, final String owner, final String name, final String descriptor) { int opcodeAndSource = opcode | (api < Opcodes.ASM5 ? Opcodes.SOURCE_DEPRECATED : 0); diff --git a/asm/src/main/java/org/objectweb/asm/Opcodes.java b/asm/src/main/java/org/objectweb/asm/Opcodes.java index 5b51acf4..41543ccf 100644 --- a/asm/src/main/java/org/objectweb/asm/Opcodes.java +++ b/asm/src/main/java/org/objectweb/asm/Opcodes.java @@ -56,7 +56,8 @@ public interface Opcodes { * * @deprecated This API is experimental. */ - @Deprecated int ASM10_EXPERIMENTAL = 1 << 24 | 10 << 16 | 0 << 8; + @Deprecated(forRemoval = false) + int ASM10_EXPERIMENTAL = 1 << 24 | 10 << 16 | 0 << 8; /* * Internal flags used to redirect calls to deprecated methods. For instance, if a visitOldStuff diff --git a/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java b/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java index bb9e095f..87c2822c 100644 --- a/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java +++ b/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java @@ -651,7 +651,7 @@ void testReadAndWrite_computeMaxs(final PrecompiledClass classParameter, final A @ParameterizedTest @MethodSource(ALL_CLASSES_AND_ALL_APIS) void testReadAndWrite_computeMaxs_newInstance( - final PrecompiledClass classParameter, final Api apiParameter) throws Exception { + final PrecompiledClass classParameter, final Api apiParameter) { byte[] classFile = classParameter.getBytes(); ClassReader classReader = new ClassReader(classFile); ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); diff --git a/asm/src/test/java/org/objectweb/asm/ConstantsTest.java b/asm/src/test/java/org/objectweb/asm/ConstantsTest.java index c746e2e8..1c375ad9 100644 --- a/asm/src/test/java/org/objectweb/asm/ConstantsTest.java +++ b/asm/src/test/java/org/objectweb/asm/ConstantsTest.java @@ -62,7 +62,7 @@ private enum ConstantType { VERIFICATION_TYPE_INFO_TAG, OPCODE, OTHER - }; + } @Test void testAsmVersions() { diff --git a/asm/src/test/java/org/objectweb/asm/MethodVisitorTest.java b/asm/src/test/java/org/objectweb/asm/MethodVisitorTest.java index 915b1b4d..e389eb78 100644 --- a/asm/src/test/java/org/objectweb/asm/MethodVisitorTest.java +++ b/asm/src/test/java/org/objectweb/asm/MethodVisitorTest.java @@ -440,7 +440,7 @@ private static class MethodVisitor4Override extends MethodVisitor { this.log = log; } - @Deprecated + @Deprecated(forRemoval = false) @Override public void visitMethodInsn( final int opcode, final String owner, final String name, final String descriptor) { diff --git a/asm/src/test/java/org/objectweb/asm/signature/SignaturesProviders.java b/asm/src/test/java/org/objectweb/asm/signature/SignaturesProviders.java index 650db55f..d274b891 100644 --- a/asm/src/test/java/org/objectweb/asm/signature/SignaturesProviders.java +++ b/asm/src/test/java/org/objectweb/asm/signature/SignaturesProviders.java @@ -28,7 +28,7 @@ public final class SignaturesProviders { AsmTest.allClassesAndLatestApi() .map(argument -> (PrecompiledClass) argument.get()[0]) .filter(precompiledClass -> !precompiledClass.isMoreRecentThan(AsmTest.Api.ASM7)) - .forEach(precompiledClass -> collectSignatures(precompiledClass)); + .forEach(SignaturesProviders::collectSignatures); assertFalse(CLASS_SIGNATURES.isEmpty()); assertFalse(FIELD_SIGNATURES.isEmpty()); assertFalse(METHOD_SIGNATURES.isEmpty()); diff --git a/benchmarks/src/jmh/java/org/objectweb/asm/benchmarks/AsmAdapter.java b/benchmarks/src/jmh/java/org/objectweb/asm/benchmarks/AsmAdapter.java index 078dbd71..5fa6d14f 100644 --- a/benchmarks/src/jmh/java/org/objectweb/asm/benchmarks/AsmAdapter.java +++ b/benchmarks/src/jmh/java/org/objectweb/asm/benchmarks/AsmAdapter.java @@ -320,7 +320,7 @@ public void visitFieldInsn( } @Override - @Deprecated + @Deprecated(forRemoval = false) public void visitMethodInsn( final int opcode, final String owner, final String name, final String descriptor) { ++count; From 3f11dd7df50a7b0d40f468414c3d547944de3139 Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sun, 2 Nov 2025 10:20:54 +0100 Subject: [PATCH 19/23] Fix code smells found with SonarQube. --- .../AnalyzerWithBasicInterpreterTest.java | 1 - .../AnalyzerWithSimpleVerifierTest.java | 4 +- .../asm/tree/analysis/MethodNodeBuilder.java | 3 +- .../asm/tree/analysis/SmallSetTest.java | 4 +- .../org/objectweb/asm/commons/Remapper.java | 14 +++---- .../objectweb/asm/commons/SimpleRemapper.java | 9 +++++ .../asm/commons/AdviceAdapterTest.java | 5 +-- .../asm/commons/AnalyzerAdapterTest.java | 14 +++---- .../asm/commons/ClassRemapperTest.java | 2 + .../objectweb/asm/commons/CodeComment.java | 6 --- .../asm/commons/GeneratorAdapterTest.java | 2 +- .../asm/commons/JsrInlinerAdapterTest.java | 4 +- .../asm/commons/LambdaRemapTest.java | 39 ++++++++++--------- .../asm/commons/LocalVariablesSorterTest.java | 3 +- .../asm/commons/TryCatchBlockSorterTest.java | 4 +- .../org/objectweb/asm/test/ClassFileTest.java | 8 ++-- .../org/objectweb/asm/tree/ClassNodeTest.java | 16 ++++++-- .../org/objectweb/asm/tree/InsnListTest.java | 6 +-- .../asm/util/CheckMethodAdapter.java | 7 ++-- .../org/objectweb/asm/util/Textifier.java | 6 +++ .../asm/util/CheckClassAdapterTest.java | 6 +-- .../asm/util/CheckMethodAdapterTest.java | 16 ++++---- .../asm/util/CheckSignatureAdapterTest.java | 36 ++++++++--------- .../org/objectweb/asm/util/CodeComment.java | 4 +- .../java/org/objectweb/asm/util/Comment.java | 4 +- .../org/objectweb/asm/util/PrinterTest.java | 2 +- .../asm/util/SignaturesProviders.java | 2 +- .../java/org/objectweb/asm/Attribute.java | 1 + .../org/objectweb/asm/ClassReaderTest.java | 12 ++++-- .../org/objectweb/asm/ClassVisitorTest.java | 8 +++- .../asm/ClassWriterComputeMaxsTest.java | 3 +- .../org/objectweb/asm/ClassWriterTest.java | 6 +-- .../test/java/org/objectweb/asm/TypeTest.java | 14 ++++++- 33 files changed, 156 insertions(+), 115 deletions(-) diff --git a/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerWithBasicInterpreterTest.java b/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerWithBasicInterpreterTest.java index 3719d7fa..987cbc48 100644 --- a/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerWithBasicInterpreterTest.java +++ b/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerWithBasicInterpreterTest.java @@ -46,7 +46,6 @@ import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import org.objectweb.asm.test.AsmTest; -import org.objectweb.asm.test.AsmTest.PrecompiledClass; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; diff --git a/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerWithSimpleVerifierTest.java b/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerWithSimpleVerifierTest.java index 97789abe..985b8c6a 100644 --- a/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerWithSimpleVerifierTest.java +++ b/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/AnalyzerWithSimpleVerifierTest.java @@ -193,7 +193,7 @@ void testAnalyze_classNotFound() { } @Test - void testAnalyze_mergeStackFrames() throws AnalyzerException { + void testAnalyze_mergeStackFrames() { Label loopLabel = new Label(); MethodNode methodNode = new MethodNodeBuilder(1, 4) @@ -223,7 +223,7 @@ void testAnalyze_mergeStackFrames() throws AnalyzerException { } @Test - void testAnalyze_mergeStackFramesWithExceptionHandlers() throws AnalyzerException { + void testAnalyze_mergeStackFramesWithExceptionHandlers() { Label startTry0Label = new Label(); Label endTry0Label = new Label(); Label catch0Label = new Label(); diff --git a/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/MethodNodeBuilder.java b/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/MethodNodeBuilder.java index 6051488a..26e33c66 100644 --- a/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/MethodNodeBuilder.java +++ b/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/MethodNodeBuilder.java @@ -94,8 +94,7 @@ MethodNodeBuilder nop() { } MethodNodeBuilder push() { - methodNode.visitInsn(Opcodes.ICONST_0); - return this; + return iconst_0(); } MethodNodeBuilder pop() { diff --git a/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/SmallSetTest.java b/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/SmallSetTest.java index 818b0040..dfe28af4 100644 --- a/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/SmallSetTest.java +++ b/asm-analysis/src/test/java/org/objectweb/asm/tree/analysis/SmallSetTest.java @@ -149,7 +149,7 @@ void testIterator_next_noSuchElement() { iterator.next(); iterator.next(); - Executable next = () -> iterator.next(); + Executable next = iterator::next; assertThrows(NoSuchElementException.class, next); } @@ -159,7 +159,7 @@ void testIterator_remove() { Iterator iterator = newSmallSet(ELEMENT1, ELEMENT2).iterator(); iterator.next(); - assertThrows(UnsupportedOperationException.class, () -> iterator.remove()); + assertThrows(UnsupportedOperationException.class, iterator::remove); } private static SmallSet newSmallSet(final Object element1, final Object element2) { diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java index e17462f3..f44a3adc 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java @@ -69,7 +69,7 @@ public abstract class Remapper { * @deprecated use {@link #Remapper(int)} instead. */ @Deprecated(forRemoval = false) - public Remapper() { + protected Remapper() { this.api = 0; } @@ -79,7 +79,7 @@ public Remapper() { * @param api the ASM API version supported by this remapper. Must be one of the {@code * ASM}x values in {@link Opcodes}. */ - public Remapper(final int api) { + protected Remapper(final int api) { if (api != Opcodes.ASM9 && api != Opcodes.ASM8 && api != Opcodes.ASM7 @@ -333,11 +333,11 @@ public String mapInnerClassName( } else { int originSplit = name.lastIndexOf('/'); int remappedSplit = remappedInnerName.lastIndexOf('/'); - if (originSplit != -1 && remappedSplit != -1) { - if (name.substring(originSplit).equals(remappedInnerName.substring(remappedSplit))) { - // class name not changed - return innerName; - } + if (originSplit != -1 + && remappedSplit != -1 + && name.substring(originSplit).equals(remappedInnerName.substring(remappedSplit))) { + // class name not changed + return innerName; } } diff --git a/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java b/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java index f2df3cbb..8e2121a3 100644 --- a/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java +++ b/asm-commons/src/main/java/org/objectweb/asm/commons/SimpleRemapper.java @@ -129,7 +129,16 @@ public String mapMethodName(final String owner, final String name, final String return remappedName == null ? name : remappedName; } + /** + * Maps an invokedynamic or a constant dynamic method name to its new name. + * + * @param name the name of the method. + * @param descriptor the descriptor of the method. + * @return the new name of the method. + * @deprecated use {@link #mapInvokeDynamicMethodName(String, String, Handle, Object...)} instead. + */ @Override + @Deprecated(forRemoval = false) public String mapInvokeDynamicMethodName(final String name, final String descriptor) { String remappedName = map('.' + name + descriptor); return remappedName == null ? name : remappedName; diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/AdviceAdapterTest.java b/asm-commons/src/test/java/org/objectweb/asm/commons/AdviceAdapterTest.java index dcf7625d..bcfc20a8 100644 --- a/asm-commons/src/test/java/org/objectweb/asm/commons/AdviceAdapterTest.java +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/AdviceAdapterTest.java @@ -634,7 +634,7 @@ void testAllMethods_constructorWithHandlerFallthroughToPrivateMethodCall() { @ParameterizedTest @MethodSource(ALL_CLASSES_AND_ALL_APIS) void testAllMethods_precompiledClass( - final PrecompiledClass classParameter, final Api apiParameter) throws Exception { + final PrecompiledClass classParameter, final Api apiParameter) { ClassReader classReader = new ClassReader(classParameter.getBytes()); ClassWriter classWriter = new ClassWriter(0); ClassVisitor adviceClassAdapter = @@ -678,9 +678,6 @@ protected void onMethodEnter() { // pop from an empty stack because the previous ICONST_1 was not simulated. visitJumpInsn(IFEQ, label); } - - @Override - protected void onMethodExit(final int opcode) {} }; adviceAdapter.visitCode(); diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/AnalyzerAdapterTest.java b/asm-commons/src/test/java/org/objectweb/asm/commons/AnalyzerAdapterTest.java index 9866c009..f284e33a 100644 --- a/asm-commons/src/test/java/org/objectweb/asm/commons/AnalyzerAdapterTest.java +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/AnalyzerAdapterTest.java @@ -95,7 +95,7 @@ void testVisitFrame_invalidFrameType() { @ParameterizedTest @MethodSource(ALL_CLASSES_AND_ALL_APIS) void testAllMethods_precompiledClass( - final PrecompiledClass classParameter, final Api apiParameter) throws Exception { + final PrecompiledClass classParameter, final Api apiParameter) { byte[] classFile = classParameter.getBytes(); ClassReader classReader = new ClassReader(classFile); ClassWriter classWriter = new ClassWriter(0); @@ -201,13 +201,11 @@ public void visitFrame( private void maybeInsertFrame() { // Don't insert a frame if we already have one for this instruction, from the original class. - if (!hasOriginalFrame) { - if (analyzerAdapter.locals != null && analyzerAdapter.stack != null) { - ArrayList local = toFrameTypes(analyzerAdapter.locals); - ArrayList stack = toFrameTypes(analyzerAdapter.stack); - super.visitFrame( - Opcodes.F_NEW, local.size(), local.toArray(), stack.size(), stack.toArray()); - } + if (!hasOriginalFrame && analyzerAdapter.locals != null && analyzerAdapter.stack != null) { + ArrayList local = toFrameTypes(analyzerAdapter.locals); + ArrayList stack = toFrameTypes(analyzerAdapter.stack); + super.visitFrame( + Opcodes.F_NEW, local.size(), local.toArray(), stack.size(), stack.toArray()); } hasOriginalFrame = false; } diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/ClassRemapperTest.java b/asm-commons/src/test/java/org/objectweb/asm/commons/ClassRemapperTest.java index 75616189..a969619f 100644 --- a/asm-commons/src/test/java/org/objectweb/asm/commons/ClassRemapperTest.java +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/ClassRemapperTest.java @@ -291,12 +291,14 @@ public String map(final String internalName) { @Test void testInvokeDynamicInsn_field_deprecated() { ClassNode classNode = new ClassNode(); + @SuppressWarnings("deprecation") ClassRemapper classRemapper = new ClassRemapper( /* latest api */ Opcodes.ASM9, classNode, new Remapper() { @Override + @Deprecated public String mapInvokeDynamicMethodName(final String name, final String descriptor) { return "new." + name; } diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/CodeComment.java b/asm-commons/src/test/java/org/objectweb/asm/commons/CodeComment.java index f56ba446..2723e981 100644 --- a/asm-commons/src/test/java/org/objectweb/asm/commons/CodeComment.java +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/CodeComment.java @@ -75,10 +75,4 @@ protected ByteVector write( final int maxLocals) { return new ByteVector(); } - - @Override - protected Label[] getLabels() { - super.getLabels(); - return new Label[] {new Label()}; - } } diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/GeneratorAdapterTest.java b/asm-commons/src/test/java/org/objectweb/asm/commons/GeneratorAdapterTest.java index 6b551dc0..41543d47 100644 --- a/asm-commons/src/test/java/org/objectweb/asm/commons/GeneratorAdapterTest.java +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/GeneratorAdapterTest.java @@ -232,7 +232,7 @@ void testLoadThis() { void testLoadThis_illegalState() { Generator generator = new Generator(Opcodes.ACC_STATIC, "m", "()V"); - Executable loadThis = () -> generator.loadThis(); + Executable loadThis = generator::loadThis; assertThrows(IllegalStateException.class, loadThis); } diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/JsrInlinerAdapterTest.java b/asm-commons/src/test/java/org/objectweb/asm/commons/JsrInlinerAdapterTest.java index a96eff5e..e17d6d85 100644 --- a/asm-commons/src/test/java/org/objectweb/asm/commons/JsrInlinerAdapterTest.java +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/JsrInlinerAdapterTest.java @@ -1511,9 +1511,9 @@ void testInlineJsr_precompiledClass( ClassFile classFile = new ClassFile(classWriter.toByteArray()); if (classParameter.isNotCompatibleWithCurrentJdk()) { - assertThrows(UnsupportedClassVersionError.class, () -> classFile.newInstance()); + assertThrows(UnsupportedClassVersionError.class, classFile::newInstance); } else { - assertDoesNotThrow(() -> classFile.newInstance()); + assertDoesNotThrow(classFile::newInstance); } } diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/LambdaRemapTest.java b/asm-commons/src/test/java/org/objectweb/asm/commons/LambdaRemapTest.java index ed017e62..c5557cd2 100644 --- a/asm-commons/src/test/java/org/objectweb/asm/commons/LambdaRemapTest.java +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/LambdaRemapTest.java @@ -32,25 +32,27 @@ class LambdaRemapTest extends AsmTest implements Opcodes { + "[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;"; /* - public class LambdaTest { - public interface TestInterface { - default void helloWorld() { - } - } - - public static void normalLambda() { - Runnable runnable = Thread::dumpStack; - } + * The 3 following methods correspond to the following Java code: + * + * public class LambdaTest { + * public interface TestInterface { + * default void helloWorld() {} + * } + * + * public static void normalLambda() { + * Runnable runnable = Thread::dumpStack; + * } + * + * public static void serializableLambda() { + * Runnable runnable = (Runnable & Serializable) Thread::dumpStack; + * } + * + * public static void advancedLambda() { + * Runnable runnable = (Runnable & Serializable & TestInterface) Thread::dumpStack; + * } + * } + */ - public static void serializableLambda() { - Runnable runnable = (Runnable & Serializable) Thread::dumpStack; - } - - public static void advancedLambda() { - Runnable runnable = (Runnable & Serializable & TestInterface) Thread::dumpStack; - } - } - */ void func_normalLambda(final MethodVisitor methodVisitor) { methodVisitor.visitInvokeDynamicInsn( "run", @@ -135,6 +137,7 @@ public String map(final String internalName) { "java/lang/Runnable.run()V", "call"))); } + @SuppressWarnings("deprecation") public static Stream remappersDeprecated() { return Stream.of( new Remapper() { diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/LocalVariablesSorterTest.java b/asm-commons/src/test/java/org/objectweb/asm/commons/LocalVariablesSorterTest.java index bafcedb7..8ddadea0 100644 --- a/asm-commons/src/test/java/org/objectweb/asm/commons/LocalVariablesSorterTest.java +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/LocalVariablesSorterTest.java @@ -32,7 +32,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; @@ -215,7 +214,7 @@ void testAllMethods_precompiledClass( } @Test - void testAllMethods_issue317586() throws FileNotFoundException, IOException { + void testAllMethods_issue317586() throws IOException { ClassReader classReader = new ClassReader(Files.newInputStream(Paths.get("src/test/resources/Issue317586.class"))); ClassWriter classWriter = new ClassWriter(0); diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/TryCatchBlockSorterTest.java b/asm-commons/src/test/java/org/objectweb/asm/commons/TryCatchBlockSorterTest.java index e66689d0..36c178ac 100644 --- a/asm-commons/src/test/java/org/objectweb/asm/commons/TryCatchBlockSorterTest.java +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/TryCatchBlockSorterTest.java @@ -86,9 +86,9 @@ public MethodVisitor visitMethod( ClassFile classFile = new ClassFile(classWriter.toByteArray()); if (classParameter.isNotCompatibleWithCurrentJdk()) { - assertThrows(UnsupportedClassVersionError.class, () -> classFile.newInstance()); + assertThrows(UnsupportedClassVersionError.class, classFile::newInstance); } else { - assertDoesNotThrow(() -> classFile.newInstance()); + assertDoesNotThrow(classFile::newInstance); } } } diff --git a/asm-test/src/test/java/org/objectweb/asm/test/ClassFileTest.java b/asm-test/src/test/java/org/objectweb/asm/test/ClassFileTest.java index e2e2a13b..ce7726ab 100644 --- a/asm-test/src/test/java/org/objectweb/asm/test/ClassFileTest.java +++ b/asm-test/src/test/java/org/objectweb/asm/test/ClassFileTest.java @@ -36,7 +36,7 @@ void testGetConstantPoolDump() { void testNewInstance_validClass(final PrecompiledClass classParameter, final Api apiParameter) { ClassFile classFile = new ClassFile(classParameter.getBytes()); - Executable newInstance = () -> classFile.newInstance(); + Executable newInstance = classFile::newInstance; if (classParameter.isNotCompatibleWithCurrentJdk()) { assertThrows(UnsupportedClassVersionError.class, newInstance); @@ -51,7 +51,7 @@ void testNewInstance_validClass(final PrecompiledClass classParameter, final Api void testNewInstance_invalidClass(final InvalidClass invalidClass) { ClassFile classFile = new ClassFile(invalidClass.getBytes()); - Executable newInstance = () -> classFile.newInstance(); + Executable newInstance = classFile::newInstance; assertThrows(ClassFormatException.class, newInstance); } @@ -123,7 +123,7 @@ void testHashcode_invalidClass() { InvalidClass invalidClass = InvalidClass.INVALID_CLASS_VERSION; ClassFile classFile = new ClassFile(invalidClass.getBytes()); - Executable hashCode = () -> classFile.hashCode(); + Executable hashCode = classFile::hashCode; Exception exception = assertThrows(ClassFormatException.class, hashCode); assertEquals("Unsupported class version", exception.getMessage()); @@ -144,7 +144,7 @@ void testToString_invalidClass() { InvalidClass invalidClass = InvalidClass.INVALID_CLASS_VERSION; ClassFile classFile = new ClassFile(invalidClass.getBytes()); - Executable toString = () -> classFile.toString(); + Executable toString = classFile::toString; Exception exception = assertThrows(ClassFormatException.class, toString); assertEquals("Unsupported class version", exception.getMessage()); diff --git a/asm-tree/src/test/java/org/objectweb/asm/tree/ClassNodeTest.java b/asm-tree/src/test/java/org/objectweb/asm/tree/ClassNodeTest.java index c51a041d..6834ba96 100644 --- a/asm-tree/src/test/java/org/objectweb/asm/tree/ClassNodeTest.java +++ b/asm-tree/src/test/java/org/objectweb/asm/tree/ClassNodeTest.java @@ -195,13 +195,19 @@ public ModuleVisitor visitModule(final String name, final int access, final Stri } @Override - public void visitNestHost(final String nestHost) {} + public void visitNestHost(final String nestHost) { + // Remove nest host. + } @Override - public void visitNestMember(final String nestMember) {} + public void visitNestMember(final String nestMember) { + // Remove nest member. + } @Override - public void visitPermittedSubclass(final String permittedSubclass) {} + public void visitPermittedSubclass(final String permittedSubclass) { + // Remove permitted subclass. + } @Override public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { @@ -218,7 +224,9 @@ public AnnotationVisitor visitTypeAnnotation( } @Override - public void visitAttribute(final Attribute attribute) {} + public void visitAttribute(final Attribute attribute) { + // Remove attribute. + } @Override public RecordComponentVisitor visitRecordComponent( diff --git a/asm-tree/src/test/java/org/objectweb/asm/tree/InsnListTest.java b/asm-tree/src/test/java/org/objectweb/asm/tree/InsnListTest.java index 93f033a0..76996e82 100644 --- a/asm-tree/src/test/java/org/objectweb/asm/tree/InsnListTest.java +++ b/asm-tree/src/test/java/org/objectweb/asm/tree/InsnListTest.java @@ -126,7 +126,7 @@ public void visitInsn(final int opcode) { void testIteratorNext_noSuchElement() { ListIterator iterator = newInsnList().iterator(); - Executable next = () -> iterator.next(); + Executable next = iterator::next; assertThrows(NoSuchElementException.class, next); } @@ -149,7 +149,7 @@ void testIteratorNext_nonEmptyList() { void testIteratorPrevious_noSuchElement() { ListIterator iterator = newInsnList().iterator(); - Executable previous = () -> iterator.previous(); + Executable previous = iterator::previous; assertThrows(NoSuchElementException.class, previous); } @@ -194,7 +194,7 @@ void testIteratorRemove_illegalState() { InsnList insnList = newInsnList(insn1, insn2); ListIterator iterator = insnList.iterator(1); - Executable remove = () -> iterator.remove(); + Executable remove = iterator::remove; assertThrows(IllegalStateException.class, remove); } diff --git a/asm-util/src/main/java/org/objectweb/asm/util/CheckMethodAdapter.java b/asm-util/src/main/java/org/objectweb/asm/util/CheckMethodAdapter.java index ed18dc00..158ce528 100644 --- a/asm-util/src/main/java/org/objectweb/asm/util/CheckMethodAdapter.java +++ b/asm-util/src/main/java/org/objectweb/asm/util/CheckMethodAdapter.java @@ -290,6 +290,7 @@ private enum Method { private static final String MUST_NOT_BE_NULL_OR_EMPTY = " (must not be null or empty)"; private static final String START_LABEL = "start label"; private static final String END_LABEL = "end label"; + private static final String LABEL = "label"; /** The class version number. */ public int version; @@ -787,7 +788,7 @@ public void visitJumpInsn(final int opcode, final Label label) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); checkOpcodeMethod(opcode, Method.VISIT_JUMP_INSN); - checkLabel(label, /* checkVisited= */ false, "label"); + checkLabel(label, /* checkVisited= */ false, LABEL); super.visitJumpInsn(opcode, label); ++insnCount; } @@ -796,7 +797,7 @@ public void visitJumpInsn(final int opcode, final Label label) { public void visitLabel(final Label label) { checkVisitCodeCalled(); checkVisitMaxsNotCalled(); - checkLabel(label, /* checkVisited= */ false, "label"); + checkLabel(label, /* checkVisited= */ false, LABEL); if (labelInsnIndices.get(label) != null) { throw new IllegalStateException("Already visited label"); } @@ -1093,7 +1094,7 @@ private void checkFrameValue(final Object value) { if (value instanceof String) { checkInternalName(version, (String) value, "Invalid stack frame value"); } else if (value instanceof Label) { - checkLabel((Label) value, /* checkVisited= */ false, "label"); + checkLabel((Label) value, /* checkVisited= */ false, LABEL); } else { throw new IllegalArgumentException("Invalid stack frame value: " + value); } diff --git a/asm-util/src/main/java/org/objectweb/asm/util/Textifier.java b/asm-util/src/main/java/org/objectweb/asm/util/Textifier.java index b8dcb072..d13a32f8 100644 --- a/asm-util/src/main/java/org/objectweb/asm/util/Textifier.java +++ b/asm-util/src/main/java/org/objectweb/asm/util/Textifier.java @@ -1443,6 +1443,12 @@ protected void appendLabel(final Label label) { stringBuilder.append(name); } + /** + * Appends a string representation of the given handle to {@link #stringBuilder}. + * + * @param handle a handle. + * @deprecated use {@link #appendHandle(Handle, String)} instead. + */ @Deprecated(forRemoval = false) protected void appendHandle(final Handle handle) { appendHandle(handle, tab3); diff --git a/asm-util/src/test/java/org/objectweb/asm/util/CheckClassAdapterTest.java b/asm-util/src/test/java/org/objectweb/asm/util/CheckClassAdapterTest.java index 3b00a498..e9b2a3b4 100644 --- a/asm-util/src/test/java/org/objectweb/asm/util/CheckClassAdapterTest.java +++ b/asm-util/src/test/java/org/objectweb/asm/util/CheckClassAdapterTest.java @@ -380,7 +380,7 @@ void testVisitMethod_checkDataFlowByDefault() { methodVisitor.visitInsn(IRETURN); methodVisitor.visitMaxs(0, 0); - Executable visitEnd = () -> methodVisitor.visitEnd(); + Executable visitEnd = methodVisitor::visitEnd; Exception exception = assertThrows(IllegalArgumentException.class, visitEnd); assertTrue( @@ -403,7 +403,7 @@ void testVisitMethod_checkMaxStackAndLocalsIfClassWriterWithoutComputeMaxs() { methodVisitor.visitInsn(IRETURN); methodVisitor.visitMaxs(0, 2); - Executable visitEnd = () -> methodVisitor.visitEnd(); + Executable visitEnd = methodVisitor::visitEnd; Exception exception = assertThrows(IllegalArgumentException.class, visitEnd); assertTrue( @@ -423,7 +423,7 @@ void testVisitMethod_noDataFlowCheckIfDisabled() { methodVisitor.visitInsn(IRETURN); methodVisitor.visitMaxs(0, 0); - Executable visitEnd = () -> methodVisitor.visitEnd(); + Executable visitEnd = methodVisitor::visitEnd; assertDoesNotThrow(visitEnd); } diff --git a/asm-util/src/test/java/org/objectweb/asm/util/CheckMethodAdapterTest.java b/asm-util/src/test/java/org/objectweb/asm/util/CheckMethodAdapterTest.java index 37567c0c..c923f483 100644 --- a/asm-util/src/test/java/org/objectweb/asm/util/CheckMethodAdapterTest.java +++ b/asm-util/src/test/java/org/objectweb/asm/util/CheckMethodAdapterTest.java @@ -125,7 +125,7 @@ void testVisitCode_abstractMethod() { CheckMethodAdapter checkAbstractMethodAdapter = new CheckMethodAdapter(Opcodes.ACC_ABSTRACT, "m", "()V", null, Map.of()); - Executable visitCode = () -> checkAbstractMethodAdapter.visitCode(); + Executable visitCode = checkAbstractMethodAdapter::visitCode; Exception exception = assertThrows(UnsupportedOperationException.class, visitCode); assertEquals("Abstract methods cannot have code", exception.getMessage()); @@ -1109,7 +1109,7 @@ void testVisitEnd_invalidDataFlow() { dataFlowCheckMethodAdapter.visitInsn(IRETURN); dataFlowCheckMethodAdapter.visitMaxs(0, 0); - Executable visitEnd = () -> dataFlowCheckMethodAdapter.visitEnd(); + Executable visitEnd = dataFlowCheckMethodAdapter::visitEnd; Exception exception = assertThrows(IllegalArgumentException.class, visitEnd); assertTrue( @@ -1136,7 +1136,7 @@ void testVisitEnd_noInvalidMaxStackIfClassWriterWithComputeMaxs() { dataFlowCheckMethodAdapter.visitInsn(IRETURN); dataFlowCheckMethodAdapter.visitMaxs(0, 2); - Executable visitEnd = () -> dataFlowCheckMethodAdapter.visitEnd(); + Executable visitEnd = dataFlowCheckMethodAdapter::visitEnd; assertDoesNotThrow(visitEnd); } @@ -1157,7 +1157,7 @@ void testVisitEnd_noInvalidMaxStackIfClassWriterWithComputeFrames() { dataFlowCheckMethodAdapter.visitInsn(IRETURN); dataFlowCheckMethodAdapter.visitMaxs(0, 2); - Executable visitEnd = () -> dataFlowCheckMethodAdapter.visitEnd(); + Executable visitEnd = dataFlowCheckMethodAdapter::visitEnd; assertDoesNotThrow(visitEnd); } @@ -1178,7 +1178,7 @@ void testVisitEnd_invalidMaxStackIfClassWriterWithoutComputeMaxsOrComputeFrames( dataFlowCheckMethodAdapter.visitInsn(IRETURN); dataFlowCheckMethodAdapter.visitMaxs(0, 2); - Executable visitEnd = () -> dataFlowCheckMethodAdapter.visitEnd(); + Executable visitEnd = dataFlowCheckMethodAdapter::visitEnd; Exception exception = assertThrows(IllegalArgumentException.class, visitEnd); assertTrue( @@ -1207,7 +1207,7 @@ void testVisitEnd_noMissingFrameIfClassWriterWithComputeFrames() { dataFlowCheckMethodAdapter.visitInsn(IRETURN); dataFlowCheckMethodAdapter.visitMaxs(1, 2); - Executable visitEnd = () -> dataFlowCheckMethodAdapter.visitEnd(); + Executable visitEnd = dataFlowCheckMethodAdapter::visitEnd; assertDoesNotThrow(visitEnd); } @@ -1232,7 +1232,7 @@ void testVisitEnd_missingFrameIfClassWriterWithoutComputeFrames() { dataFlowCheckMethodAdapter.visitInsn(IRETURN); dataFlowCheckMethodAdapter.visitMaxs(1, 2); - Executable visitEnd = () -> dataFlowCheckMethodAdapter.visitEnd(); + Executable visitEnd = dataFlowCheckMethodAdapter::visitEnd; Exception exception = assertThrows(IllegalArgumentException.class, visitEnd); assertTrue( @@ -1250,7 +1250,7 @@ void testVisitEnd_invalidReturnType() { dataFlowCheckMethodAdapter.visitInsn(IRETURN); dataFlowCheckMethodAdapter.visitMaxs(1, 2); - Executable visitEnd = () -> dataFlowCheckMethodAdapter.visitEnd(); + Executable visitEnd = dataFlowCheckMethodAdapter::visitEnd; Exception exception = assertThrows(IllegalArgumentException.class, visitEnd); assertTrue( diff --git a/asm-util/src/test/java/org/objectweb/asm/util/CheckSignatureAdapterTest.java b/asm-util/src/test/java/org/objectweb/asm/util/CheckSignatureAdapterTest.java index 95cb8e8c..518ae783 100644 --- a/asm-util/src/test/java/org/objectweb/asm/util/CheckSignatureAdapterTest.java +++ b/asm-util/src/test/java/org/objectweb/asm/util/CheckSignatureAdapterTest.java @@ -73,7 +73,7 @@ void testVisitClassBound_classSignature_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.CLASS_SIGNATURE, null); - Executable visitClassBound = () -> checkSignatureAdapter.visitClassBound(); + Executable visitClassBound = checkSignatureAdapter::visitClassBound; assertThrows(IllegalStateException.class, visitClassBound); } @@ -83,7 +83,7 @@ void testVisitClassBound_typeSignature_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.TYPE_SIGNATURE, null); - Executable visitClassBound = () -> checkSignatureAdapter.visitClassBound(); + Executable visitClassBound = checkSignatureAdapter::visitClassBound; assertThrows(IllegalStateException.class, visitClassBound); } @@ -93,7 +93,7 @@ void testVisitInterfaceBound_classSignature_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.CLASS_SIGNATURE, null); - Executable visitInterfaceBound = () -> checkSignatureAdapter.visitInterfaceBound(); + Executable visitInterfaceBound = checkSignatureAdapter::visitInterfaceBound; assertThrows(IllegalStateException.class, visitInterfaceBound); } @@ -103,7 +103,7 @@ void testVisitInterfaceBound_typeSignature_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.TYPE_SIGNATURE, null); - Executable visitInterfaceBound = () -> checkSignatureAdapter.visitInterfaceBound(); + Executable visitInterfaceBound = checkSignatureAdapter::visitInterfaceBound; assertThrows(IllegalStateException.class, visitInterfaceBound); } @@ -114,7 +114,7 @@ void testVisitSuperClass_classSignature_illegalState() { new CheckSignatureAdapter(CheckSignatureAdapter.CLASS_SIGNATURE, null); checkSignatureAdapter.visitSuperclass(); - Executable visitSuperClass = () -> checkSignatureAdapter.visitSuperclass(); + Executable visitSuperClass = checkSignatureAdapter::visitSuperclass; assertThrows(IllegalStateException.class, visitSuperClass); } @@ -124,7 +124,7 @@ void testVisitSuperClass_methodSignature_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.METHOD_SIGNATURE, null); - Executable visitSuperClass = () -> checkSignatureAdapter.visitSuperclass(); + Executable visitSuperClass = checkSignatureAdapter::visitSuperclass; assertThrows(IllegalStateException.class, visitSuperClass); } @@ -134,7 +134,7 @@ void testVisitInterface_classSignature_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.CLASS_SIGNATURE, null); - Executable visitInterface = () -> checkSignatureAdapter.visitInterface(); + Executable visitInterface = checkSignatureAdapter::visitInterface; assertThrows(IllegalStateException.class, visitInterface); } @@ -144,7 +144,7 @@ void testVisitInterface_methodSignature_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.METHOD_SIGNATURE, null); - Executable visitInterface = () -> checkSignatureAdapter.visitInterface(); + Executable visitInterface = checkSignatureAdapter::visitInterface; assertThrows(IllegalStateException.class, visitInterface); } @@ -154,7 +154,7 @@ void testVisitParameterType_classSignature_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.CLASS_SIGNATURE, null); - Executable visitParameterType = () -> checkSignatureAdapter.visitParameterType(); + Executable visitParameterType = checkSignatureAdapter::visitParameterType; assertThrows(IllegalStateException.class, visitParameterType); } @@ -165,7 +165,7 @@ void testVisitParameterType_methodSignature_illegalState() { new CheckSignatureAdapter(CheckSignatureAdapter.METHOD_SIGNATURE, null); checkSignatureAdapter.visitReturnType(); - Executable visitParameterType = () -> checkSignatureAdapter.visitParameterType(); + Executable visitParameterType = checkSignatureAdapter::visitParameterType; assertThrows(IllegalStateException.class, visitParameterType); } @@ -175,7 +175,7 @@ void testVisitReturnType_classSignature_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.CLASS_SIGNATURE, null); - Executable visitReturnType = () -> checkSignatureAdapter.visitReturnType(); + Executable visitReturnType = checkSignatureAdapter::visitReturnType; assertThrows(IllegalStateException.class, visitReturnType); } @@ -186,7 +186,7 @@ void testVisitReturnType_methodSignature_illegalState() { new CheckSignatureAdapter(CheckSignatureAdapter.METHOD_SIGNATURE, null); checkSignatureAdapter.visitReturnType(); - Executable visitReturnType = () -> checkSignatureAdapter.visitReturnType(); + Executable visitReturnType = checkSignatureAdapter::visitReturnType; assertThrows(IllegalStateException.class, visitReturnType); } @@ -196,7 +196,7 @@ void testVisitExceptionType_classSignature_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.CLASS_SIGNATURE, null); - Executable visitExceptionType = () -> checkSignatureAdapter.visitExceptionType(); + Executable visitExceptionType = checkSignatureAdapter::visitExceptionType; assertThrows(IllegalStateException.class, visitExceptionType); } @@ -206,7 +206,7 @@ void testVisitExceptionType_methodSignature_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.METHOD_SIGNATURE, null); - Executable visitExceptionType = () -> checkSignatureAdapter.visitExceptionType(); + Executable visitExceptionType = checkSignatureAdapter::visitExceptionType; assertThrows(IllegalStateException.class, visitExceptionType); } @@ -314,7 +314,7 @@ void testVisitArrayType_classSignature_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.CLASS_SIGNATURE, null); - Executable visitArrayType = () -> checkSignatureAdapter.visitArrayType(); + Executable visitArrayType = checkSignatureAdapter::visitArrayType; assertThrows(IllegalStateException.class, visitArrayType); } @@ -325,7 +325,7 @@ void testVisitArrayType_typeSignature_illegalState() { new CheckSignatureAdapter(CheckSignatureAdapter.TYPE_SIGNATURE, null); checkSignatureAdapter.visitArrayType(); - Executable visitArrayType = () -> checkSignatureAdapter.visitArrayType(); + Executable visitArrayType = checkSignatureAdapter::visitArrayType; assertThrows(IllegalStateException.class, visitArrayType); } @@ -411,7 +411,7 @@ void testVisitTypeArgument_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.TYPE_SIGNATURE, null); - Executable visitTypeArgument = () -> checkSignatureAdapter.visitTypeArgument(); + Executable visitTypeArgument = checkSignatureAdapter::visitTypeArgument; assertThrows(IllegalStateException.class, visitTypeArgument); } @@ -443,7 +443,7 @@ void testVisitEnd_illegalState() { CheckSignatureAdapter checkSignatureAdapter = new CheckSignatureAdapter(CheckSignatureAdapter.TYPE_SIGNATURE, null); - Executable visitEnd = () -> checkSignatureAdapter.visitEnd(); + Executable visitEnd = checkSignatureAdapter::visitEnd; assertThrows(IllegalStateException.class, visitEnd); } diff --git a/asm-util/src/test/java/org/objectweb/asm/util/CodeComment.java b/asm-util/src/test/java/org/objectweb/asm/util/CodeComment.java index 86ffe56c..9ba043de 100644 --- a/asm-util/src/test/java/org/objectweb/asm/util/CodeComment.java +++ b/asm-util/src/test/java/org/objectweb/asm/util/CodeComment.java @@ -88,5 +88,7 @@ public void asmify( } @Override - public void textify(final StringBuilder stringBuilder, final Map labelNames) {} + public void textify(final StringBuilder stringBuilder, final Map labelNames) { + stringBuilder.append("(empty)"); + } } diff --git a/asm-util/src/test/java/org/objectweb/asm/util/Comment.java b/asm-util/src/test/java/org/objectweb/asm/util/Comment.java index 31bbb2c6..1b9b3baf 100644 --- a/asm-util/src/test/java/org/objectweb/asm/util/Comment.java +++ b/asm-util/src/test/java/org/objectweb/asm/util/Comment.java @@ -83,7 +83,9 @@ public void asmify( } @Override - public void textify(final StringBuilder stringBuilder, final Map labelNames) {} + public void textify(final StringBuilder stringBuilder, final Map labelNames) { + stringBuilder.append("(empty)"); + } @Override public String toString() { diff --git a/asm-util/src/test/java/org/objectweb/asm/util/PrinterTest.java b/asm-util/src/test/java/org/objectweb/asm/util/PrinterTest.java index 0905eb76..5da8c2a5 100644 --- a/asm-util/src/test/java/org/objectweb/asm/util/PrinterTest.java +++ b/asm-util/src/test/java/org/objectweb/asm/util/PrinterTest.java @@ -172,7 +172,7 @@ void testVisitProvide_unsupportedByDefault() { void testVisitModuleEnd_unsupportedByDefault() { Printer printer = new EmptyPrinter(/* latest */ Opcodes.ASM10_EXPERIMENTAL); - Executable visitModuleEnd = () -> printer.visitModuleEnd(); + Executable visitModuleEnd = printer::visitModuleEnd; Exception exception = assertThrows(UnsupportedOperationException.class, visitModuleEnd); assertEquals(UNSUPPORTED_OPERATION_MESSAGE, exception.getMessage()); diff --git a/asm-util/src/test/java/org/objectweb/asm/util/SignaturesProviders.java b/asm-util/src/test/java/org/objectweb/asm/util/SignaturesProviders.java index fdc28a1d..26bbeebf 100644 --- a/asm-util/src/test/java/org/objectweb/asm/util/SignaturesProviders.java +++ b/asm-util/src/test/java/org/objectweb/asm/util/SignaturesProviders.java @@ -28,7 +28,7 @@ public final class SignaturesProviders { AsmTest.allClassesAndLatestApi() .map(argument -> (PrecompiledClass) argument.get()[0]) .filter(precompiledClass -> !precompiledClass.isMoreRecentThan(AsmTest.Api.ASM7)) - .forEach(precompiledClass -> collectSignatures(precompiledClass)); + .forEach(SignaturesProviders::collectSignatures); assertFalse(CLASS_SIGNATURES.isEmpty()); assertFalse(FIELD_SIGNATURES.isEmpty()); assertFalse(METHOD_SIGNATURES.isEmpty()); diff --git a/asm/src/main/java/org/objectweb/asm/Attribute.java b/asm/src/main/java/org/objectweb/asm/Attribute.java index 6fe2f425..5c42c77c 100644 --- a/asm/src/main/java/org/objectweb/asm/Attribute.java +++ b/asm/src/main/java/org/objectweb/asm/Attribute.java @@ -174,6 +174,7 @@ public static Attribute read( * ClassReader overrides {@link ClassReader#readLabel}. Hence {@link #read(ClassReader, int, int, * char[], int, Label[])} must not manually create {@link Label} instances. * + * @param classReader the class that contains the attribute to be read. * @param bytecodeOffset a bytecode offset in a method. * @param labels the already created labels, indexed by their offset. If a label already exists * for bytecodeOffset this method does not create a new one. Otherwise it stores the new label diff --git a/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java b/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java index 8e483462..d98d361e 100644 --- a/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java +++ b/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java @@ -418,13 +418,19 @@ public MethodVisitor visitMethod( } @Override - public void visitNestHost(final String nestHost) {} + public void visitNestHost(final String nestHost) { + // Skip nest host. + } @Override - public void visitNestMember(final String nestMember) {} + public void visitNestMember(final String nestMember) { + // Skip nest member. + } @Override - public void visitPermittedSubclass(final String permittedSubclass) {} + public void visitPermittedSubclass(final String permittedSubclass) { + // Skip permitted subclass. + } }; Executable accept = () -> classReader.accept(classVisitor, 0); diff --git a/asm/src/test/java/org/objectweb/asm/ClassVisitorTest.java b/asm/src/test/java/org/objectweb/asm/ClassVisitorTest.java index 5c04868f..695b3fcf 100644 --- a/asm/src/test/java/org/objectweb/asm/ClassVisitorTest.java +++ b/asm/src/test/java/org/objectweb/asm/ClassVisitorTest.java @@ -267,10 +267,14 @@ public ModuleVisitor visitModule( return new ModuleVisitor(api, super.visitModule(name, access, version)) { @Override - public void visitMainClass(final String mainClass) {} + public void visitMainClass(final String mainClass) { + // Remove main class. + } @Override - public void visitPackage(final String packaze) {} + public void visitPackage(final String packaze) { + // Remove package. + } @Override public void visitRequire( diff --git a/asm/src/test/java/org/objectweb/asm/ClassWriterComputeMaxsTest.java b/asm/src/test/java/org/objectweb/asm/ClassWriterComputeMaxsTest.java index 363ff000..c871ccea 100644 --- a/asm/src/test/java/org/objectweb/asm/ClassWriterComputeMaxsTest.java +++ b/asm/src/test/java/org/objectweb/asm/ClassWriterComputeMaxsTest.java @@ -1042,8 +1042,7 @@ TestCaseBuilder nop() { } TestCaseBuilder push() { - methodVisitor.visitInsn(Opcodes.ICONST_0); - return this; + return iconst_0(); } TestCaseBuilder push2() { diff --git a/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java b/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java index 87c2822c..9fed5dac 100644 --- a/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java +++ b/asm/src/test/java/org/objectweb/asm/ClassWriterTest.java @@ -325,7 +325,7 @@ void testToByteArray_constantPoolSizeTooLarge(final int constantPoolCount) { classWriter.newConst(Integer.valueOf(i)); } - Executable toByteArray = () -> classWriter.toByteArray(); + Executable toByteArray = classWriter::toByteArray; if (constantPoolCount > 65535) { ClassTooLargeException exception = assertThrows(ClassTooLargeException.class, toByteArray); @@ -353,7 +353,7 @@ void testToByteArray_methodCodeSizeTooLarge(final int methodCodeSize) { methodVisitor.visitMaxs(0, 0); methodVisitor.visitEnd(); - Executable toByteArray = () -> classWriter.toByteArray(); + Executable toByteArray = classWriter::toByteArray; if (methodCodeSize > 65535) { MethodTooLargeException exception = assertThrows(MethodTooLargeException.class, toByteArray); @@ -391,7 +391,7 @@ void testToByteArray_computeMaxs_largeSubroutines(final String classFileName) th ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); classReader.accept(classWriter, attributes(), 0); - Executable toByteArray = () -> classWriter.toByteArray(); + Executable toByteArray = classWriter::toByteArray; assertDoesNotThrow(toByteArray); } diff --git a/asm/src/test/java/org/objectweb/asm/TypeTest.java b/asm/src/test/java/org/objectweb/asm/TypeTest.java index ccefc808..adacaa48 100644 --- a/asm/src/test/java/org/objectweb/asm/TypeTest.java +++ b/asm/src/test/java/org/objectweb/asm/TypeTest.java @@ -381,7 +381,7 @@ void testGetDescriptorFromClass() { } @Test - void testGetOpcode() { + void testGetOpcode_ialoadOrStore() { assertEquals(BALOAD, Type.BOOLEAN_TYPE.getOpcode(IALOAD)); assertEquals(BALOAD, Type.BYTE_TYPE.getOpcode(IALOAD)); assertEquals(CALOAD, Type.CHAR_TYPE.getOpcode(IALOAD)); @@ -395,6 +395,10 @@ void testGetOpcode() { assertEquals(AASTORE, Type.getType("Ljava/lang/Object;").getOpcode(IASTORE)); assertEquals(AASTORE, Type.getObjectType("java/lang/Object").getOpcode(IASTORE)); assertEquals(AASTORE, Type.getType("[I").getOpcode(IASTORE)); + } + + @Test + void testGetOpcode_return() { assertEquals(RETURN, Type.VOID_TYPE.getOpcode(Opcodes.IRETURN)); assertEquals(IRETURN, Type.BOOLEAN_TYPE.getOpcode(Opcodes.IRETURN)); assertEquals(IRETURN, Type.BYTE_TYPE.getOpcode(Opcodes.IRETURN)); @@ -409,6 +413,10 @@ void testGetOpcode() { assertEquals(ARETURN, Type.getType("Ljava/lang/Object;").getOpcode(Opcodes.IRETURN)); assertEquals(ARETURN, Type.getObjectType("java/lang/Object").getOpcode(Opcodes.IRETURN)); assertEquals(ARETURN, Type.getType("[I").getOpcode(Opcodes.IRETURN)); + } + + @Test + void testGetOpcode_iadd() { assertEquals(IADD, Type.BOOLEAN_TYPE.getOpcode(IADD)); assertEquals(IADD, Type.BYTE_TYPE.getOpcode(IADD)); assertEquals(IADD, Type.CHAR_TYPE.getOpcode(IADD)); @@ -417,6 +425,10 @@ void testGetOpcode() { assertEquals(FADD, Type.FLOAT_TYPE.getOpcode(IADD)); assertEquals(LADD, Type.LONG_TYPE.getOpcode(IADD)); assertEquals(DADD, Type.DOUBLE_TYPE.getOpcode(IADD)); + } + + @Test + void testGetOpcode_unsupported() { Class expectedException = UnsupportedOperationException.class; assertThrows(expectedException, () -> Type.VOID_TYPE.getOpcode(IADD)); assertThrows(expectedException, () -> Type.VOID_TYPE.getOpcode(ILOAD)); From ec9f0b2bdd255f352f26b0bd921fee6647accd4e Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sun, 30 Nov 2025 13:05:26 +0100 Subject: [PATCH 20/23] Fix OOM error when reading invalid class. --- asm/src/main/java/org/objectweb/asm/ClassReader.java | 3 +++ .../test/java/org/objectweb/asm/ClassReaderTest.java | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/asm/src/main/java/org/objectweb/asm/ClassReader.java b/asm/src/main/java/org/objectweb/asm/ClassReader.java index ca5d8b2e..3a7a055c 100644 --- a/asm/src/main/java/org/objectweb/asm/ClassReader.java +++ b/asm/src/main/java/org/objectweb/asm/ClassReader.java @@ -3539,6 +3539,9 @@ private Attribute readAttribute( final char[] charBuffer, final int codeAttributeOffset, final Label[] labels) { + if (length > classFileBuffer.length - offset) { + throw new IllegalArgumentException(); + } for (Attribute attributePrototype : attributePrototypes) { if (attributePrototype.type.equals(type)) { return attributePrototype.read( diff --git a/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java b/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java index d98d361e..3174f174 100644 --- a/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java +++ b/asm/src/test/java/org/objectweb/asm/ClassReaderTest.java @@ -41,6 +41,7 @@ import java.io.IOException; import java.io.InputStream; import java.time.Duration; +import java.util.Base64; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -636,6 +637,17 @@ public void visit( assertEquals(Opcodes.V_PREVIEW, classVersion.get() & Opcodes.V_PREVIEW); } + @Test + void testAccept_invalidCustomAttribute() { + byte[] input = Base64.getDecoder().decode("IftdBAAAAAAAAgEAAAD/AAAAAAAAAAAAAAIBAAF/////"); + ClassReader reader = new ClassReader(input); + ClassVisitor noOpVisitor = new ClassVisitor(Opcodes.ASM9) {}; + + Executable accept = () -> reader.accept(noOpVisitor, ClassReader.EXPAND_FRAMES); + + assertThrows(IllegalArgumentException.class, accept); + } + private static class EmptyClassVisitor extends ClassVisitor { final AnnotationVisitor annotationVisitor = From f389709b71d6c16785d15cda34824cb879b1c75b Mon Sep 17 00:00:00 2001 From: Jonah Graham Date: Sat, 6 Dec 2025 14:57:43 +0000 Subject: [PATCH 21/23] Provide an upper bound on Import-Package --- build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b1a59f4e..ff2cb93b 100644 --- a/build.gradle +++ b/build.gradle @@ -48,6 +48,7 @@ dependencies { allprojects { group = 'org.ow2.asm' version = '9.10' + (rootProject.hasProperty('release') ? '' : '-SNAPSHOT') + ext.osgi_version_upper_bound = '9.11' } subprojects { @@ -324,7 +325,7 @@ configure(subprojects.findAll{it.provides}) { if (imports) { jar.manifest.attributes( 'Import-Package': - imports.collect{"${it};version=\"${version}\""}.join(','), + imports.collect{"${it};version=\"[${version},${ext.osgi_version_upper_bound})\""}.join(','), 'Module-Requires': transitiveRequires().collect{"${it};transitive=true"}.join(',')) } From 4e6de6cb3dcddb00a2bbf7bbb9ee40f0c15281c5 Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sat, 6 Dec 2025 16:06:06 +0100 Subject: [PATCH 22/23] Update version to 9.9.1 for new minor release. --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index ff2cb93b..28190b06 100644 --- a/build.gradle +++ b/build.gradle @@ -47,8 +47,8 @@ dependencies { allprojects { group = 'org.ow2.asm' - version = '9.10' + (rootProject.hasProperty('release') ? '' : '-SNAPSHOT') - ext.osgi_version_upper_bound = '9.11' + version = '9.9.1' + (rootProject.hasProperty('release') ? '' : '-SNAPSHOT') + ext.osgi_version_upper_bound = '9.10' } subprojects { From c9d66aa89efcec8c85b23f5814f3ef81c8140eb0 Mon Sep 17 00:00:00 2001 From: Eric Bruneton Date: Sat, 6 Dec 2025 16:19:15 +0100 Subject: [PATCH 23/23] Update to next release version number. --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 28190b06..ff2cb93b 100644 --- a/build.gradle +++ b/build.gradle @@ -47,8 +47,8 @@ dependencies { allprojects { group = 'org.ow2.asm' - version = '9.9.1' + (rootProject.hasProperty('release') ? '' : '-SNAPSHOT') - ext.osgi_version_upper_bound = '9.10' + version = '9.10' + (rootProject.hasProperty('release') ? '' : '-SNAPSHOT') + ext.osgi_version_upper_bound = '9.11' } subprojects {