asm issueshttps://gitlab.ow2.org/asm/asm/-/issues2023-12-20T13:59:35Zhttps://gitlab.ow2.org/asm/asm/-/issues/318010reset() methods for the Tree API2023-12-20T13:59:35ZAura Leereset() methods for the Tree APICurrently, when 'xyz.accept(cn)' is called twice for a ClassNode, everything will be duplicated inside that ClassNode.
A 'reset()' method in each Node of the Tree API would be a neat feature to have, in order to overwrite all current i...Currently, when 'xyz.accept(cn)' is called twice for a ClassNode, everything will be duplicated inside that ClassNode.
A 'reset()' method in each Node of the Tree API would be a neat feature to have, in order to overwrite all current information of a Node with new information (using accept), without the need of recreating the node and replacing all existing Node references.
For ClassNode it would look like something like this:
```java
public void reset() {
this.methods.clear();
this.fields.clear();
this.version = 0;
this.access = 0;
this.name = null;
this.signature = null;
this.superName = null;
this.interfaces = new ArrayList<>();
this.sourceFile = null;
this.sourceDebug = null;
this.module = null;
this.outerClass = null;
this.outerMethod = null;
this.outerMethodDesc = null;
this.visibleAnnotations = null;
this.invisibleAnnotations = null;
this.visibleTypeAnnotations = null;
this.invisibleTypeAnnotations = null;
this.attrs = null;
this.innerClasses = new ArrayList<>();
this.nestHostClass = null;
this.nestMembers = null;
this.permittedSubclasses = null;
this.recordComponents = null;
}
```
Currently, I have my own implementation of ClassNode with the specified reset() method, but when ASM is updated, I would have to add new fields to this method.https://gitlab.ow2.org/asm/asm/-/issues/318005Is there an opportunity to improve the JDK major release upgrade experience?2023-10-21T13:41:59ZDanny ThomasIs there an opportunity to improve the JDK major release upgrade experience?Hello! Danny from the Netflix JVM Ecosystem team here. I just finished our readiness work for JDK 21 and our friends `ClassReader` and `ClassVisitor` were responsible for the majority of issues we've seen across many libraries, except fo...Hello! Danny from the Netflix JVM Ecosystem team here. I just finished our readiness work for JDK 21 and our friends `ClassReader` and `ClassVisitor` were responsible for the majority of issues we've seen across many libraries, except for one compatibility issue in Lombok due to changes in javac. It's a problem exacerbated by the common practice of shading ASM, which means though the project is quite proactive about adding support for the next release, we can't just force an upgrade to ASM-latest, we're coupled to releases of the libraries themselves. Just off the top of my head there's shaded ASM in:
- Byte Buddy (own experimental property)
- Guice
- Groovy
- Gradle Test Retry Plugin
- JaCoCo
- Javassist
- Jersey
- Spring Framework (forked, but still has version checks but set to latest version, experimental check in ClassVisitor patched out)
With JDK releases accelerated to the extent they are now this is something everyone is dealing with far more regularly than years past. It's doubly a problem for our team, not only getting ready for a GA releases, but because we're often testing on ea, nightly or custom builds, and we need to build and canary against real applications. To unblock us, I had to build an `asm-all` dependency that takes `spring-core` ASM and copies it it to every known shaded asm package and inject it first on the classpath. While updating that library today to test [a PR that's particularly important to us](https://github.com/openjdk/jdk/pull/15718) and ASM breaking the build, tests, runtime and Jenkins code coverage plugins, but after hacking in the upgrade everything working great, I wondered to myself if these strict version checks are really necessary, and perhaps the same safety could be guaranteed while throwing an error only when a class file is truly unreadable/incompatible.
That approach would have seen us through JDK 16 to 22, and perhaps beyond without an error. So, I thought I'd come and ask what your current thinking about this is. Are the strict version/experimental checks really necessary, or is it safe to throw an incompatible version exception when encountering unknown constant tags, opcodes, etc.? I understand the format itself imposes those restrictions, because the sizes of each block needs to be known ahead of time. I guess there must have been changes that don't involve new block types, but still aren't backwards compatible, generic type annotations maybe?
With the `ClassFile` API is still a long time away from being available, not to mention adopted broadly, I think any improvement to the JDK upgrade experience w/ ASM would be a real boon to the ecosystem.
We use Gradle, so I was considering doing what we did for the JakartaEE migration: we use Gradle transforms in our [gradle-jakartaee-migration-plugin](https://github.com/nebula-plugins/gradle-jakartaee-migration-plugin), we would just upgrade ASM on the fly during artifact resolution. That's saved us a _huge_ amount of trouble, as we have a lot of legacy code with `javax` references, and made roman riding two legacy frameworks, plus Spring Boot 2 and Spring Boot 3 at the same time a cinch. It'd be very little effort to build (in fact the Tomcat JakartaEE migration tool is a fine streaming and recursive shading implementation, I'd just use as an implementation detail). It'd would work for dependencies of both the build and the project, but would only benefit Gradle users, so thought I'd ask here first before I solve this problem for us, when there might an opportunity to help out here instead.
If you think there is an opportunity here, some advice on what kind of confidence/quality bar we'd need to meet to allow the removal of these specific version and experimental version checks would be all I'd need to get started.
Appreciate your time. Cheers!Eric BrunetonEric Brunetonhttps://gitlab.ow2.org/asm/asm/-/issues/318002asm-analysis Frame allocates an array unnecessarily inside executeInvokeInsn2023-09-23T17:58:20ZTagir Valeevasm-analysis Frame allocates an array unnecessarily inside executeInvokeInsnWhen profiling my application, I've noticed that more than 1% of my analysis CPU time is spent on this line:
https://gitlab.ow2.org/asm/asm/-/blob/master/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Frame.java?ref_type=head...When profiling my application, I've noticed that more than 1% of my analysis CPU time is spent on this line:
https://gitlab.ow2.org/asm/asm/-/blob/master/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Frame.java?ref_type=heads#L675
```
int i = Type.getArgumentTypes(methodDescriptor).length
```
Here, we create a complete array of argument types (sometimes, along with `Type` objects themselves), only to read its length. This looks wasteful. It would be nice to have a method inside `Type` or somewhere else, which simply returns the number of arguments, so it could be used here. Something like this:
```
diff --git a/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Frame.java b/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Frame.java
--- a/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Frame.java (revision 2e22cefc43c0d274d97b7e0135e97bb4825b9e85)
+++ b/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Frame.java (date 1694700910626)
@@ -672,7 +672,7 @@
final AbstractInsnNode insn, final String methodDescriptor, final Interpreter<V> interpreter)
throws AnalyzerException {
ArrayList<V> valueList = new ArrayList<>();
- for (int i = Type.getArgumentTypes(methodDescriptor).length; i > 0; --i) {
+ for (int i = Type.getArgumentCount(methodDescriptor); i > 0; --i) {
valueList.add(0, pop());
}
if (insn.getOpcode() != Opcodes.INVOKESTATIC && insn.getOpcode() != Opcodes.INVOKEDYNAMIC) {
Index: asm/src/main/java/org/objectweb/asm/Type.java
===================================================================
diff --git a/asm/src/main/java/org/objectweb/asm/Type.java b/asm/src/main/java/org/objectweb/asm/Type.java
--- a/asm/src/main/java/org/objectweb/asm/Type.java (revision 2e22cefc43c0d274d97b7e0135e97bb4825b9e85)
+++ b/asm/src/main/java/org/objectweb/asm/Type.java (date 1694700872437)
@@ -295,21 +295,8 @@
*/
public static Type[] getArgumentTypes(final String methodDescriptor) {
// First step: compute the number of argument types in methodDescriptor.
- int numArgumentTypes = 0;
- // Skip the first character, which is always a '('.
- int currentOffset = 1;
- // Parse the argument types, one at a each loop iteration.
- while (methodDescriptor.charAt(currentOffset) != ')') {
- while (methodDescriptor.charAt(currentOffset) == '[') {
- currentOffset++;
- }
- if (methodDescriptor.charAt(currentOffset++) == 'L') {
- // Skip the argument descriptor content.
- int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
- currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
- }
- ++numArgumentTypes;
- }
+ int numArgumentTypes = getArgumentCount(methodDescriptor);
+ int currentOffset;
// Second step: create a Type instance for each argument type.
Type[] argumentTypes = new Type[numArgumentTypes];
@@ -333,6 +320,31 @@
return argumentTypes;
}
+ /**
+ * Returns the number of arguments of the given method descriptor.
+ *
+ * @param methodDescriptor a method descriptor.
+ * @return the number of arguments of the given method descriptor.
+ */
+ public static int getArgumentCount(String methodDescriptor) {
+ int numArgumentTypes = 0;
+ // Skip the first character, which is always a '('.
+ int currentOffset = 1;
+ // Parse the argument types, one at a each loop iteration.
+ while (methodDescriptor.charAt(currentOffset) != ')') {
+ while (methodDescriptor.charAt(currentOffset) == '[') {
+ currentOffset++;
+ }
+ if (methodDescriptor.charAt(currentOffset++) == 'L') {
+ // Skip the argument descriptor content.
+ int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
+ currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
+ }
+ ++numArgumentTypes;
+ }
+ return numArgumentTypes;
+ }
+
/**
* Returns the {@link Type} values corresponding to the argument types of the given method.
*
```
Thank you in advance.https://gitlab.ow2.org/asm/asm/-/issues/318001recognize Java 22 version (66)2023-06-17T09:21:06Zcushonrecognize Java 22 version (66)ASM should be updated to recognize Java 22 version (66).
The latest upstream JDK versions are being updated to use the new version in https://github.com/openjdk/jdk/pull/13567 (which is still pending).
The previous ASM issue for versio...ASM should be updated to recognize Java 22 version (66).
The latest upstream JDK versions are being updated to use the new version in https://github.com/openjdk/jdk/pull/13567 (which is still pending).
The previous ASM issue for version 21 was https://gitlab.ow2.org/asm/asm/-/issues/317990https://gitlab.ow2.org/asm/asm/-/issues/317999Remapper removes leading $ from class name2023-05-12T16:03:09ZChristopher-Marcel EsserRemapper removes leading $ from class nameWhen using ASM's `Remapper`, the default implementation of `mapInnerClassName` removes leading dollar signs from the class name. However, Java allows a class name with `$` at the beginning, which is what, e.g., Kotlin's serialization plu...When using ASM's `Remapper`, the default implementation of `mapInnerClassName` removes leading dollar signs from the class name. However, Java allows a class name with `$` at the beginning, which is what, e.g., Kotlin's serialization plugin generates. I've encountered this issue in ASM with that serialization framework [here](https://github.com/Kotlin/kotlinx.serialization/issues/2285).
### Expected Behavior
ASM should keep the original names to ensure other tooling doesn't fail to look it up.
### Current Behavior
[Currently](https://gitlab.ow2.org/asm/asm/-/blob/9bf7d41ca5212d82b6383104d3340d3e897fea42/asm-commons/src/main/java/org/objectweb/asm/commons/Remapper.java#L287-292) leading `$`s are stripped.
To work around the issue, users of `Remapper` can override `mapInnerClassName`:
```java
@Override
public String mapInnerClassName(String name, String ownerName, String innerName) {
final String result = super.mapInnerClassName(name, ownerName, innerName);
if (innerName.startsWith("$") && !result.startsWith("$")) {
return "$" + result;
}
return result;
}
```
This workaround doesn't solve the problem if the original class name started with more than one `$`. As explained in [the linked issue in kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization/issues/2285), they actually generate a _nested_ class with a name that starts with a `$`. So ASM will initially see `$$`.
My assumption is that ASM should only remove a single `$` if it's a nested class. All other dollar signs should remain.
Am I wrong anywhere? Thanks in advance for everything - from ASM in the first place all the way to reading this issue :smile:https://gitlab.ow2.org/asm/asm/-/issues/317998Analyzer can fail to catch thrown exceptions2023-06-17T09:22:09ZHao ZhongAnalyzer can fail to catch thrown exceptionsAnalyzer.analyze has the following code:
`public Frame<V>[] analyze(...)
...
merge(0, currentFrame, null);
...
try {
...
merge(targetInsnIndex, currentFrame, subroutine);
...
} catch (AnalyzerException e) {
t...Analyzer.analyze has the following code:
`public Frame<V>[] analyze(...)
...
merge(0, currentFrame, null);
...
try {
...
merge(targetInsnIndex, currentFrame, subroutine);
...
} catch (AnalyzerException e) {
throw new AnalyzerException(
e.node, "Error at instruction " + insnIndex + ": " + e.getMessage(), e);
} catch (RuntimeException e) {
// DontCheck(IllegalCatch): can't be fixed, for backward compatibility.
throw new AnalyzerException(
insnNode, "Error at instruction " + insnIndex + ": " + e.getMessage(), e);
}
}
`
Here, merge(0, currentFrame, null) can throw AnalyzerException, but it is not enclosed in the try statement. As a result, its thrown exceptions are not handled like other merges like merge(targetInsnIndex, currentFrame, subroutine);https://gitlab.ow2.org/asm/asm/-/issues/317994ConstantDynamic instances should not be canonicalized as they define distinct...2023-04-06T17:51:28ZSøren GjesseConstantDynamic instances should not be canonicalized as they define distinct constantsI would like to construct the class file shown at the bottom of this issue. However, that is not possible with ASM. The issue arises from the two `ldc` instructions using a dynamic constant using the exact same BSM and arguments to that ...I would like to construct the class file shown at the bottom of this issue. However, that is not possible with ASM. The issue arises from the two `ldc` instructions using a dynamic constant using the exact same BSM and arguments to that BSM seen in the two methods `getConstant1` and `getConstant2`:
```
public static java.lang.Object getConstant1();
descriptor: ()Ljava/lang/Object;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: ldc #27 // Dynamic #0:constantName:Ljava/lang/Object;
2: areturn
LineNumberTable:
line 135: 0
public static java.lang.Object getConstant2();
descriptor: ()Ljava/lang/Object;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: ldc #29 // Dynamic #1:constantName:Ljava/lang/Object;
2: areturn
LineNumberTable:
line 139: 0
```
When trying to generate these methods using ASM:
```
{
methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "getConstant1", "()Ljava/lang/Object;", null, null);
methodVisitor.visitCode();
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(135, label0);
methodVisitor.visitLdcInsn(new ConstantDynamic("constantName", "Ljava/lang/Object;", new Handle(Opcodes.H_INVOKESTATIC, "A", "myConstant", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;", false), new Object[] {}));
methodVisitor.visitInsn(ARETURN);
methodVisitor.visitMaxs(1, 0);
methodVisitor.visitEnd();
}
{
methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "getConstant2", "()Ljava/lang/Object;", null, null);
methodVisitor.visitCode();
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(139, label0);
methodVisitor.visitLdcInsn(new ConstantDynamic("constantName", "Ljava/lang/Object;", new Handle(Opcodes.H_INVOKESTATIC, "A", "myConstant", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;", false), new Object[] {}));
methodVisitor.visitInsn(ARETURN);
methodVisitor.visitMaxs(1, 0);
methodVisitor.visitEnd();
}
```
the two `new ConstantDynamic` taking the exact same arguments will be canonicalized during writing and the generated class file will not have two dynamic constants but only one.
To actually generate the class file below I did a crude patch on ASM (see [here](https://r8.googlesource.com/r8/+/9baf2a528d42b1b238605f73568d589b0b32fb83/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicMultipleConstantsWithDifferentSymbolicReferenceUsingSameBSMAndArgumentsTest.java#544)), which is not a general solution.
Full javap output of the class file (the bytes for it is [here](https://r8.googlesource.com/r8/+/9baf2a528d42b1b238605f73568d589b0b32fb83/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicMultipleConstantsWithDifferentSymbolicReferenceUsingSameBSMAndArgumentsTest.java#318)):
```
Classfile A.class
Last modified Mar 17, 2023; size 1364 bytes
SHA-256 checksum 4379e359d727521479cee1aa5b6d711090b2777553454d9b667fe502a1b0daa9
Compiled from "A.java"
public class A
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #2 // A
super_class: #4 // java/lang/Object
interfaces: 0, fields: 0, methods: 5, attributes: 3
Constant pool:
#1 = Utf8 A
#2 = Class #1 // A
#3 = Utf8 java/lang/Object
#4 = Class #3 // java/lang/Object
#5 = Utf8 A.java
#6 = Utf8 java/lang/invoke/MethodHandles$Lookup
#7 = Class #6 // java/lang/invoke/MethodHandles$Lookup
#8 = Utf8 java/lang/invoke/MethodHandles
#9 = Class #8 // java/lang/invoke/MethodHandles
#10 = Utf8 Lookup
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = NameAndType #11:#12 // "<init>":()V
#14 = Methodref #4.#13 // java/lang/Object."<init>":()V
#15 = Utf8 this
#16 = Utf8 LA;
#17 = Utf8 getConstant1
#18 = Utf8 ()Ljava/lang/Object;
#19 = Utf8 myConstant
#20 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
#21 = NameAndType #19:#20 // myConstant:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
#22 = Methodref #2.#21 // A.myConstant:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
#23 = MethodHandle 6:#22 // REF_invokeStatic A.myConstant:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
#24 = Utf8 constantName
#25 = Utf8 Ljava/lang/Object;
#26 = NameAndType #24:#25 // constantName:Ljava/lang/Object;
#27 = Dynamic #0:#26 // #0:constantName:Ljava/lang/Object;
#28 = Utf8 getConstant2
#29 = Dynamic #1:#26 // #1:constantName:Ljava/lang/Object;
#30 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class<*>;)Ljava/lang/Object;
#31 = Utf8 lookup
#32 = Utf8 Ljava/lang/invoke/MethodHandles$Lookup;
#33 = Utf8 name
#34 = Utf8 Ljava/lang/String;
#35 = Utf8 type
#36 = Utf8 Ljava/lang/Class<*>;
#37 = Utf8 Ljava/lang/Class;
#38 = Utf8 main
#39 = Utf8 ([Ljava/lang/String;)V
#40 = Utf8 java/lang/System
#41 = Class #40 // java/lang/System
#42 = Utf8 out
#43 = Utf8 Ljava/io/PrintStream;
#44 = NameAndType #42:#43 // out:Ljava/io/PrintStream;
#45 = Fieldref #41.#44 // java/lang/System.out:Ljava/io/PrintStream;
#46 = NameAndType #17:#18 // getConstant1:()Ljava/lang/Object;
#47 = Methodref #2.#46 // A.getConstant1:()Ljava/lang/Object;
#48 = NameAndType #28:#18 // getConstant2:()Ljava/lang/Object;
#49 = Methodref #2.#48 // A.getConstant2:()Ljava/lang/Object;
#50 = Utf8 java/io/PrintStream
#51 = Class #50 // java/io/PrintStream
#52 = Utf8 [Ljava/lang/String;
#53 = Class #52 // "[Ljava/lang/String;"
#54 = Utf8 println
#55 = Utf8 (Z)V
#56 = NameAndType #54:#55 // println:(Z)V
#57 = Methodref #51.#56 // java/io/PrintStream.println:(Z)V
#58 = Utf8 Code
#59 = Utf8 LineNumberTable
#60 = Utf8 LocalVariableTable
#61 = Utf8 LocalVariableTypeTable
#62 = Utf8 Signature
#63 = Utf8 StackMapTable
#64 = Utf8 InnerClasses
#65 = Utf8 SourceFile
#66 = Utf8 BootstrapMethods
{
public A();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #14 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 132: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LA;
public static java.lang.Object getConstant1();
descriptor: ()Ljava/lang/Object;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: ldc #27 // Dynamic #0:constantName:Ljava/lang/Object;
2: areturn
LineNumberTable:
line 135: 0
public static java.lang.Object getConstant2();
descriptor: ()Ljava/lang/Object;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: ldc #29 // Dynamic #1:constantName:Ljava/lang/Object;
2: areturn
LineNumberTable:
line 139: 0
private static java.lang.Object myConstant(java.lang.invoke.MethodHandles$Lookup, java.lang.String, java.lang.Class<?>);
descriptor: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=3, args_size=3
0: new #4 // class java/lang/Object
3: dup
4: invokespecial #14 // Method java/lang/Object."<init>":()V
7: areturn
LineNumberTable:
line 143: 0
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 lookup Ljava/lang/invoke/MethodHandles$Lookup;
0 8 1 name Ljava/lang/String;
0 8 2 type Ljava/lang/Class;
LocalVariableTypeTable:
Start Length Slot Name Signature
0 8 2 type Ljava/lang/Class<*>;
Signature: #30 // (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class<*>;)Ljava/lang/Object;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
0: getstatic #45 // Field java/lang/System.out:Ljava/io/PrintStream;
3: invokestatic #47 // Method getConstant1:()Ljava/lang/Object;
6: invokestatic #49 // Method getConstant2:()Ljava/lang/Object;
9: if_acmpne 16
12: iconst_1
13: goto 17
16: iconst_0
17: invokevirtual #57 // Method java/io/PrintStream.println:(Z)V
20: return
StackMapTable: number_of_entries = 2
frame_type = 80 // same_locals_1_stack_item
stack = [ class java/io/PrintStream ]
frame_type = 255 // full_frame
offset_delta = 0
locals = [ class "[Ljava/lang/String;" ]
stack = [ class java/io/PrintStream, int ]
LineNumberTable:
line 12: 0
line 13: 20
}
InnerClasses:
public static final #10= #7 of #9; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
SourceFile: "A.java"
BootstrapMethods:
0: #23 REF_invokeStatic A.myConstant:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
Method arguments:
1: #23 REF_invokeStatic A.myConstant:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
Method arguments:
```https://gitlab.ow2.org/asm/asm/-/issues/317993[question] Planned release date for ASM 9.52023-06-17T09:22:48ZRyan Lubke[question] Planned release date for ASM 9.5Hey folks, if there is a better forum to pose simple questions such as this, please feel free to redirect me.
We've been doing a lot of JDK 21 testing with our project as such we've been using ASM 9.5-SNAPSHOT. We may have the requireme...Hey folks, if there is a better forum to pose simple questions such as this, please feel free to redirect me.
We've been doing a lot of JDK 21 testing with our project as such we've been using ASM 9.5-SNAPSHOT. We may have the requirement to do releases of these bits in the near future, so we're wondering if there is a planned date for the release of ASM 9.5 so that we can plan accordingly?https://gitlab.ow2.org/asm/asm/-/issues/317990recognize Java 21 version (65)2023-06-01T17:36:41Zcushonrecognize Java 21 version (65)ASM should be updated to recognize Java 21 version (65).
The latest upstream JDK versions now were updated to use that class file version in https://github.com/openjdk/jdk/commit/175e3d3ff332be25cca9822c58c46f1e012953c2
The previous AS...ASM should be updated to recognize Java 21 version (65).
The latest upstream JDK versions now were updated to use that class file version in https://github.com/openjdk/jdk/commit/175e3d3ff332be25cca9822c58c46f1e012953c2
The previous ASM commit that added support for version 20 was https://gitlab.ow2.org/asm/asm/-/commit/8b7090dc7fc280aeff270473658766179cb2024b.Remi ForaxRemi Foraxhttps://gitlab.ow2.org/asm/asm/-/issues/317982Get offsets of instructions in MethodVisitor2022-12-13T07:25:22ZAo LiGet offsets of instructions in MethodVisitorI need to get offsets of instructions in MethodVisitor and I followed the implementation of `CodeSizeEvaluator`, which works for most instructions except for `LDC` and `LDC_W`. `LDC` requires 2 bytes and `LDC_W` requires 3 bytes but they...I need to get offsets of instructions in MethodVisitor and I followed the implementation of `CodeSizeEvaluator`, which works for most instructions except for `LDC` and `LDC_W`. `LDC` requires 2 bytes and `LDC_W` requires 3 bytes but they are both passed to `visitLdcInsn`.
One solution is to pass opcode to `visitLdcInsn` which may break many dependencies.
Another solution is to make `readCode` method in `ClassReader` public or protected so that I can override the method to maintain the code offset manually.
Please let me know if there is another way to solve this issue.https://gitlab.ow2.org/asm/asm/-/issues/317981Add public getDelegate method to all visitor classes.2022-10-01T15:53:55ZraphwAdd public getDelegate method to all visitor classes.Currently, only the RecordComponentVisitor offers a public method for reading the underlying visitor. Sometimes, it is however desirable to communicate with a visitor that lies beneath another visitor also for the other visitor implement...Currently, only the RecordComponentVisitor offers a public method for reading the underlying visitor. Sometimes, it is however desirable to communicate with a visitor that lies beneath another visitor also for the other visitor implementations. Could an equivalent getter be added for the other visitors, too?
Also, would you consider to make the delegate field for the record component visitor protected as with the other visitors? This can be handy when a subclass of a visitor wants to change its delegate after visiting some properties, for example after all attributes were passed. Ideally, ASM would be consistent here.Remi ForaxRemi Foraxhttps://gitlab.ow2.org/asm/asm/-/issues/317979Request: Manually add module-info.java into source tree2022-08-01T07:41:14ZMarián KončekRequest: Manually add module-info.java into source treeI am packaging this project in Fedora where we don't have access to Gradle, so we use poms from Maven Central.
We can use maven-bundle-plugin along with your bnd-module-plugin tool to generate module-info.class files.
But I don't like th...I am packaging this project in Fedora where we don't have access to Gradle, so we use poms from Maven Central.
We can use maven-bundle-plugin along with your bnd-module-plugin tool to generate module-info.class files.
But I don't like this approach as it brings in more dependencies.
I inspected your decompiled module-info.class files and I have seen that they are effectively just ~5 lines long.
Would you please consider adding module-info.java files into the source tree so that bnd-module-plugin is not necessary for module-info.class generation?Eric BrunetonEric Brunetonhttps://gitlab.ow2.org/asm/asm/-/issues/317971java 19 support?2022-06-12T09:19:20ZAndres Luukjava 19 support?ASM Latest release was a year ago and now JDK19 Early-Access Builds is relevant.
As expected currently it get's IllegalArgumentException: Unsupported class file major version 63 when trying to parse a file from there.ASM Latest release was a year ago and now JDK19 Early-Access Builds is relevant.
As expected currently it get's IllegalArgumentException: Unsupported class file major version 63 when trying to parse a file from there.https://gitlab.ow2.org/asm/asm/-/issues/317958do you mind if I start a third party maintain for asm-xml following your BSD ...2022-01-08T15:41:11ZJin Xudo you mind if I start a third party maintain for asm-xml following your BSD license?For some reason, I need to use some lib of you named asm-xml, which is deprecated, even source-codes deleted.For some reason, I need to use some lib of you named asm-xml, which is deprecated, even source-codes deleted.Eric BrunetonEric Brunetonhttps://gitlab.ow2.org/asm/asm/-/issues/317957Incorrect data returned by ClassReader.readStream2022-03-21T14:13:32ZThomas BitontiIncorrect data returned by ClassReader.readStreamWhen the class size is less than the minimum buffer size, the data array which is returned by 'readStream' is longer than the actual number of bytes which were read.
For non-valid classes (in particular, those which are truncated), this...When the class size is less than the minimum buffer size, the data array which is returned by 'readStream' is longer than the actual number of bytes which were read.
For non-valid classes (in particular, those which are truncated), this leads to misleading failures. Instead of failing to obtain a next byte from the data array, an unusual value will be obtained from the data array, leading to a failure unrelated to the root problem of retrieving of a data byte beyond the actual read data.
A potential fix would be to change this check in 'readStream':
From:
`if ( readCount == 1 ) {`
To:
`if ( (readCount == 1) && (bytesRead == bufferSize) ) {`https://gitlab.ow2.org/asm/asm/-/issues/317956Publish Maven BOM2022-03-14T10:17:56ZDavid PhillipsPublish Maven BOMIt would be great if ASM could publish a Maven [Bill of Materials (BOM)](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#bill-of-materials-bom-poms), making it easier to consume ASM and ensure the s...It would be great if ASM could publish a Maven [Bill of Materials (BOM)](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#bill-of-materials-bom-poms), making it easier to consume ASM and ensure the same version is used for all of the ASM artifacts. Consumers would use the BOM like this:
```xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-bom</artifactId>
<version>9.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
```
Then they can add dependencies for the various artifacts, without specifying versions:
```xml
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
</dependency>
</dependencies>
```
Here's an example of what a BOM looks like: https://search.maven.org/artifact/org.jdbi/jdbi3-bom/3.23.0/pomhttps://gitlab.ow2.org/asm/asm/-/issues/317947Support for JDK 18 (development started already)2022-01-08T15:38:57ZMatthias KurzSupport for JDK 18 (development started already)Work on JDK 18 has started yesterday:
https://github.com/openjdk/jdk/commit/b018c450e5e4737ccd08ed505fd06cee16c42648
As you can see in the commit the class file version was increased to 62.
Again I can not make a merge request because ...Work on JDK 18 has started yesterday:
https://github.com/openjdk/jdk/commit/b018c450e5e4737ccd08ed505fd06cee16c42648
As you can see in the commit the class file version was increased to 62.
Again I can not make a merge request because I get the error "You have reached your project limits" - but I didn't create a project. Can someone please just allow me to fork the project? Thanks!https://gitlab.ow2.org/asm/asm/-/issues/317940Add OpCodes.ASM_LATEST2021-03-13T10:22:37ZKrzysztof KrasonAdd OpCodes.ASM_LATESTSome projects (like maven plugins one, see https://github.com/apache/maven-plugin-tools/pull/32/files) explicitly need to state the supported ASM version and they use e.g. OpCodes.ASM8 to do that.
Because such projects don't upgrade oft...Some projects (like maven plugins one, see https://github.com/apache/maven-plugin-tools/pull/32/files) explicitly need to state the supported ASM version and they use e.g. OpCodes.ASM8 to do that.
Because such projects don't upgrade often there is an issue that one needs to upgrade asm independently of the project that we depend on. And this creates an issue because it is not possible if one uses OpCodes.ASM*.
My suggestion is to create a simple pointer to the latest (non-experimental) version:
e.g.:
OpCodes.ASM_LATEST = OpCodes.ASM9
And maybe:
OpCodes.ASM_EXPERIMENTAL_LATEST = OpCodes.ASM10_EXPERIMENTAL
This way if project uses that *_LATEST alias it can be sure to support the newest possible ASM.https://gitlab.ow2.org/asm/asm/-/issues/317938Lambda targets are not remapped2021-03-07T15:56:18Zx4eLambda targets are not remappedHi, I have encountered an issue with the remapping of InvokeDynamics, specifically when dealing with Javac compiled invokedynamics.
To explain I made a simple example:
```java
import org.objectweb.asm.*;
import org.objectweb.asm.commons...Hi, I have encountered an issue with the remapping of InvokeDynamics, specifically when dealing with Javac compiled invokedynamics.
To explain I made a simple example:
```java
import org.objectweb.asm.*;
import org.objectweb.asm.commons.*;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.util.*;
import java.io.PrintWriter;
public class Test {
public static void main(String[] args) throws Throwable {
ClassNode classNode = new ClassNode();
new ClassReader("Example").accept(classNode, ClassReader.SKIP_DEBUG);
print(classNode);
ClassNode newNode = new ClassNode();
Remapper remapper = new SimpleRemapper(
"Example.something()V",
"different"
);
classNode.accept(new ClassRemapper(newNode, remapper));
print(newNode);
}
public static void print(ClassNode cn) {
Textifier textifier = new Textifier();
PrintWriter writer = new PrintWriter(System.out, true);
TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, textifier, writer);
cn.accept(traceClassVisitor);
}
}
interface Example {
static void test() {
((Example) () -> System.out.println("Hello, World!")).something();
}
void something();
}
```
`Example` is a functional interface with the method `something`. A method implements the functional interface and the method `something` as a lambda.
`Test` attempts to remap the name of `something` to `nothing`. The expected behaviour would be for the method's name to change and all references to be updated.
By printing the class before and after the remapper is applied to it we can however see that this is not the case.
Before remapping:
```java
// class version 52.0 (52)
// access flags 0x600
abstract interface Example {
// access flags 0x19
public final static INNERCLASS java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup
// access flags 0x9
public static test()V
INVOKEDYNAMIC something()LExample; [
// 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
Example.lambda$test$0()V itf,
()V
]
INVOKEINTERFACE Example.something ()V (itf)
RETURN
MAXSTACK = 1
MAXLOCALS = 0
// access flags 0x401
public abstract something()V
// access flags 0x100A
private static synthetic lambda$test$0()V
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Hello, World!"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
RETURN
MAXSTACK = 2
MAXLOCALS = 0
}
```
After remapping:
```java
// class version 52.0 (52)
// access flags 0x600
abstract interface Example {
// access flags 0x19
public final static INNERCLASS java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup
// access flags 0x9
public static test()V
INVOKEDYNAMIC something()LExample; [
// 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
Example.lambda$test$0()V itf,
()V
]
INVOKEINTERFACE Example.different ()V (itf)
RETURN
MAXSTACK = 1
MAXLOCALS = 0
// access flags 0x401
public abstract different()V
// access flags 0x100A
private static synthetic lambda$test$0()V
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Hello, World!"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
RETURN
MAXSTACK = 2
MAXLOCALS = 0
}
```
As you can see the arguments of the invokedynamic instruction have not been remapped, it remains as `something()LExample;` which will result in a runtime error.
This can be fixed by providing special behaviour when remapping LambdaMetafactory invokedynamics and there is a patch implementing this here: [https://github.com/MinecraftForge/MinecraftForge/pull/3532](https://github.com/MinecraftForge/MinecraftForge/pull/3532).
Thank Youhttps://gitlab.ow2.org/asm/asm/-/issues/317937Way to retrieve methods that failed conversion in ClassWriter2021-03-07T15:56:34ZAura LeeWay to retrieve methods that failed conversion in ClassWriterWhen I write a ClassNode using ClassWriter and it fails on e.g. frame generation, I get a thrown exception, but there is no way to retrieve the method that it failed in. Can you implement a method in ClassWriter that returns the method i...When I write a ClassNode using ClassWriter and it fails on e.g. frame generation, I get a thrown exception, but there is no way to retrieve the method that it failed in. Can you implement a method in ClassWriter that returns the method it last visited?