asm issueshttps://gitlab.ow2.org/asm/asm/-/issues2024-02-28T20:25:46Zhttps://gitlab.ow2.org/asm/asm/-/issues/318011[Feature Request] A java prpoperty to override api version in *Visitor2024-02-28T20:25:46ZTsubame Matsuno[Feature Request] A java prpoperty to override api version in *VisitorI am trying to make thousands of Minecraft mods compiled in Java 8 runs on Java 21. Their old ASM api version (mostly Opcodes.ASM5) passed to super Class/Field/MethodVisitor are causing countless crashes because it makes the visitor refu...I am trying to make thousands of Minecraft mods compiled in Java 8 runs on Java 21. Their old ASM api version (mostly Opcodes.ASM5) passed to super Class/Field/MethodVisitor are causing countless crashes because it makes the visitor refuse to handle newer class format.
Currently, I am using javassist to add version check to ASM itself, but an official property option seems better.https://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/318009Unit test regression in dex2jar2023-12-21T12:42:59ZNico MexisUnit test regression in dex2jar<!---
Before creating a new issue, consider the following (this does not apply to
feature requests):
- if you need help on how to use ASM, please consult the documentation first
(https://asm.ow2.io/documentation.html). If you can't find...<!---
Before creating a new issue, consider the following (this does not apply to
feature requests):
- if you need help on how to use ASM, please consult the documentation first
(https://asm.ow2.io/documentation.html). If you can't find the answer there,
describe your problem on the http://mail.ow2.org/wws/info/asm mailing listing
instead of using GitLab issues. The mailing list has more subscribers than the
GitLab issue tracker, and you will thus likely get a quicker answer via the
mailing list.
- check if your *input* classes are valid. For this you can use "javap -p -c -v
<yourclass.class>". If this command fails, the bug is most likely in the tool
that produced this class, not in ASM. Check the javap output carefully:
sometimes the tool is able to print most of the class content, but fails to
parse some part of it. In this case there can be an error message in the middle
of the javap output, which can be easy to miss at first sight.
- check if you are using the ASM API correctly. For this, insert one or more
CheckClassAdapter instances in your class generation or class transformation
chain (in particular, in front of ClassWriter instances). If a CheckClassAdapter
throws an exception, this means that you are not using the ASM API correctly.
Fix these issues first.
If you still have a bug after completing the above steps, describe it below and
make sure to provide detailed instructions on how to reproduce it.
-->
<!--- Provide a brief summary of the issue in the title above -->
### Expected Behavior
<!--- Tell us what should happen. -->
All the given classes should yield no error when analysing them using `BasicVerifier` and `Analyzer`.
### Current Behavior
<!--- Tell us what happens instead of the expected behavior. -->
Analysing some classes using `BasicVerifier` yields an `AnalyzerException`.
An MRE is found below.
`javap` is able to analyze the classes just fine (output is also found below).
I was able to track the source of this exception down to !373.
Changing the `<=` back to `<` fixes this error; also ASM `9.5` (compared to `9.6` or `9.7-SNAPSHOT`) did not yield this error.
The error was noticed in the test suite of https://github.com/ThexXTURBOXx/dex2jar
<details>
<summary>`javap -p -c -v JSONDateConvertor.class` output</summary>
```
Classfile /C:/Coding/dex2jar/dex-translator/JSONDateConvertor.class
Last modified 01.12.2023; size 2302 bytes
SHA-256 checksum 7fe9ded8a180f4d81653168e874729b0597b570a7cb3d7a03dbd32448f8ff744
public class org.mortbay.util.ajax.JSONDateConvertor implements org.mortbay.util.ajax.JSON$Convertor
minor version: 0
major version: 50
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #2 // org/mortbay/util/ajax/JSONDateConvertor
super_class: #4 // java/lang/Object
interfaces: 1, fields: 3, methods: 6, attributes: 0
Constant pool:
#1 = Utf8 org/mortbay/util/ajax/JSONDateConvertor
#2 = Class #1 // org/mortbay/util/ajax/JSONDateConvertor
#3 = Utf8 java/lang/Object
#4 = Class #3 // java/lang/Object
#5 = Utf8 org/mortbay/util/ajax/JSON$Convertor
#6 = Class #5 // org/mortbay/util/ajax/JSON$Convertor
#7 = Utf8 _dateCache
#8 = Utf8 Lorg/mortbay/util/DateCache;
#9 = Utf8 _format
#10 = Utf8 Ljava/text/SimpleDateFormat;
#11 = Utf8 _fromJSON
#12 = Utf8 Z
#13 = Utf8 <init>
#14 = Utf8 ()V
#15 = Utf8 (Z)V
#16 = NameAndType #13:#15 // "<init>":(Z)V
#17 = Methodref #2.#16 // org/mortbay/util/ajax/JSONDateConvertor."<init>":(Z)V
#18 = Utf8 (Ljava/lang/String;Ljava/util/TimeZone;Z)V
#19 = NameAndType #13:#14 // "<init>":()V
#20 = Methodref #4.#19 // java/lang/Object."<init>":()V
#21 = Utf8 org/mortbay/util/DateCache
#22 = Class #21 // org/mortbay/util/DateCache
#23 = Utf8 (Ljava/lang/String;)V
#24 = NameAndType #13:#23 // "<init>":(Ljava/lang/String;)V
#25 = Methodref #22.#24 // org/mortbay/util/DateCache."<init>":(Ljava/lang/String;)V
#26 = NameAndType #7:#8 // _dateCache:Lorg/mortbay/util/DateCache;
#27 = Fieldref #2.#26 // org/mortbay/util/ajax/JSONDateConvertor._dateCache:Lorg/mortbay/util/DateCache;
#28 = Utf8 setTimeZone
#29 = Utf8 (Ljava/util/TimeZone;)V
#30 = NameAndType #28:#29 // setTimeZone:(Ljava/util/TimeZone;)V
#31 = Methodref #22.#30 // org/mortbay/util/DateCache.setTimeZone:(Ljava/util/TimeZone;)V
#32 = NameAndType #11:#12 // _fromJSON:Z
#33 = Fieldref #2.#32 // org/mortbay/util/ajax/JSONDateConvertor._fromJSON:Z
#34 = Utf8 java/text/SimpleDateFormat
#35 = Class #34 // java/text/SimpleDateFormat
#36 = Methodref #35.#24 // java/text/SimpleDateFormat."<init>":(Ljava/lang/String;)V
#37 = NameAndType #9:#10 // _format:Ljava/text/SimpleDateFormat;
#38 = Fieldref #2.#37 // org/mortbay/util/ajax/JSONDateConvertor._format:Ljava/text/SimpleDateFormat;
#39 = Methodref #35.#30 // java/text/SimpleDateFormat.setTimeZone:(Ljava/util/TimeZone;)V
#40 = Utf8 (Ljava/lang/String;Ljava/util/TimeZone;ZLjava/util/Locale;)V
#41 = Utf8 (Ljava/lang/String;Ljava/util/Locale;)V
#42 = NameAndType #13:#41 // "<init>":(Ljava/lang/String;Ljava/util/Locale;)V
#43 = Methodref #22.#42 // org/mortbay/util/DateCache."<init>":(Ljava/lang/String;Ljava/util/Locale;)V
#44 = Utf8 java/text/DateFormatSymbols
#45 = Class #44 // java/text/DateFormatSymbols
#46 = Utf8 (Ljava/util/Locale;)V
#47 = NameAndType #13:#46 // "<init>":(Ljava/util/Locale;)V
#48 = Methodref #45.#47 // java/text/DateFormatSymbols."<init>":(Ljava/util/Locale;)V
#49 = Utf8 (Ljava/lang/String;Ljava/text/DateFormatSymbols;)V
#50 = NameAndType #13:#49 // "<init>":(Ljava/lang/String;Ljava/text/DateFormatSymbols;)V
#51 = Methodref #35.#50 // java/text/SimpleDateFormat."<init>":(Ljava/lang/String;Ljava/text/DateFormatSymbols;)V
#52 = Utf8 DEFAULT_FORMAT
#53 = Utf8 Ljava/lang/String;
#54 = NameAndType #52:#53 // DEFAULT_FORMAT:Ljava/lang/String;
#55 = Fieldref #22.#54 // org/mortbay/util/DateCache.DEFAULT_FORMAT:Ljava/lang/String;
#56 = Utf8 GMT
#57 = String #56 // GMT
#58 = Utf8 java/util/TimeZone
#59 = Class #58 // java/util/TimeZone
#60 = Utf8 getTimeZone
#61 = Utf8 (Ljava/lang/String;)Ljava/util/TimeZone;
#62 = NameAndType #60:#61 // getTimeZone:(Ljava/lang/String;)Ljava/util/TimeZone;
#63 = Methodref #59.#62 // java/util/TimeZone.getTimeZone:(Ljava/lang/String;)Ljava/util/TimeZone;
#64 = NameAndType #13:#18 // "<init>":(Ljava/lang/String;Ljava/util/TimeZone;Z)V
#65 = Methodref #2.#64 // org/mortbay/util/ajax/JSONDateConvertor."<init>":(Ljava/lang/String;Ljava/util/TimeZone;Z)V
#66 = Utf8 fromJSON
#67 = Utf8 (Ljava/util/Map;)Ljava/lang/Object;
#68 = Utf8 java/lang/UnsupportedOperationException
#69 = Class #68 // java/lang/UnsupportedOperationException
#70 = Methodref #69.#19 // java/lang/UnsupportedOperationException."<init>":()V
#71 = Utf8 value
#72 = String #71 // value
#73 = Utf8 java/util/Map
#74 = Class #73 // java/util/Map
#75 = Utf8 get
#76 = Utf8 (Ljava/lang/Object;)Ljava/lang/Object;
#77 = NameAndType #75:#76 // get:(Ljava/lang/Object;)Ljava/lang/Object;
#78 = InterfaceMethodref #74.#77 // java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
#79 = Utf8 java/lang/String
#80 = Class #79 // java/lang/String
#81 = Utf8 parseObject
#82 = Utf8 (Ljava/lang/String;)Ljava/lang/Object;
#83 = NameAndType #81:#82 // parseObject:(Ljava/lang/String;)Ljava/lang/Object;
#84 = Methodref #35.#83 // java/text/SimpleDateFormat.parseObject:(Ljava/lang/String;)Ljava/lang/Object;
#85 = Utf8 org/mortbay/log/Log
#86 = Class #85 // org/mortbay/log/Log
#87 = Utf8 warn
#88 = Utf8 (Ljava/lang/Throwable;)V
#89 = NameAndType #87:#88 // warn:(Ljava/lang/Throwable;)V
#90 = Methodref #86.#89 // org/mortbay/log/Log.warn:(Ljava/lang/Throwable;)V
#91 = Utf8 java/lang/Exception
#92 = Class #91 // java/lang/Exception
#93 = Utf8 toJSON
#94 = Utf8 (Ljava/lang/Object;Lorg/mortbay/util/ajax/JSON$Output;)V
#95 = Utf8 java/util/Date
#96 = Class #95 // java/util/Date
#97 = Utf8 format
#98 = Utf8 (Ljava/util/Date;)Ljava/lang/String;
#99 = NameAndType #97:#98 // format:(Ljava/util/Date;)Ljava/lang/String;
#100 = Methodref #22.#99 // org/mortbay/util/DateCache.format:(Ljava/util/Date;)Ljava/lang/String;
#101 = Utf8 getClass
#102 = Utf8 ()Ljava/lang/Class;
#103 = NameAndType #101:#102 // getClass:()Ljava/lang/Class;
#104 = Methodref #4.#103 // java/lang/Object.getClass:()Ljava/lang/Class;
#105 = Utf8 org/mortbay/util/ajax/JSON$Output
#106 = Class #105 // org/mortbay/util/ajax/JSON$Output
#107 = Utf8 addClass
#108 = Utf8 (Ljava/lang/Class;)V
#109 = NameAndType #107:#108 // addClass:(Ljava/lang/Class;)V
#110 = InterfaceMethodref #106.#109 // org/mortbay/util/ajax/JSON$Output.addClass:(Ljava/lang/Class;)V
#111 = Utf8 add
#112 = Utf8 (Ljava/lang/String;Ljava/lang/Object;)V
#113 = NameAndType #111:#112 // add:(Ljava/lang/String;Ljava/lang/Object;)V
#114 = InterfaceMethodref #106.#113 // org/mortbay/util/ajax/JSON$Output.add:(Ljava/lang/String;Ljava/lang/Object;)V
#115 = Utf8 (Ljava/lang/Object;)V
#116 = NameAndType #111:#115 // add:(Ljava/lang/Object;)V
#117 = InterfaceMethodref #106.#116 // org/mortbay/util/ajax/JSON$Output.add:(Ljava/lang/Object;)V
#118 = Utf8 Code
#119 = Utf8 LineNumberTable
{
org.mortbay.util.DateCache _dateCache;
descriptor: Lorg/mortbay/util/DateCache;
flags: (0x0000)
java.text.SimpleDateFormat _format;
descriptor: Ljava/text/SimpleDateFormat;
flags: (0x0000)
private boolean _fromJSON;
descriptor: Z
flags: (0x0002) ACC_PRIVATE
public org.mortbay.util.ajax.JSONDateConvertor();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: iconst_0
2: invokespecial #17 // Method "<init>":(Z)V
5: return
LineNumberTable:
line 43: 0
line 44: 5
public org.mortbay.util.ajax.JSONDateConvertor(java.lang.String, java.util.TimeZone, boolean);
descriptor: (Ljava/lang/String;Ljava/util/TimeZone;Z)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=4, args_size=4
0: aload_0
1: invokespecial #20 // Method java/lang/Object."<init>":()V
4: aload_0
5: new #22 // class org/mortbay/util/DateCache
8: dup
9: aload_1
10: invokespecial #25 // Method org/mortbay/util/DateCache."<init>":(Ljava/lang/String;)V
13: putfield #27 // Field _dateCache:Lorg/mortbay/util/DateCache;
16: aload_0
17: getfield #27 // Field _dateCache:Lorg/mortbay/util/DateCache;
20: aload_2
21: invokevirtual #31 // Method org/mortbay/util/DateCache.setTimeZone:(Ljava/util/TimeZone;)V
24: aload_0
25: iload_3
26: putfield #33 // Field _fromJSON:Z
29: aload_0
30: new #35 // class java/text/SimpleDateFormat
33: dup
34: aload_1
35: invokespecial #36 // Method java/text/SimpleDateFormat."<init>":(Ljava/lang/String;)V
38: putfield #38 // Field _format:Ljava/text/SimpleDateFormat;
41: aload_0
42: getfield #38 // Field _format:Ljava/text/SimpleDateFormat;
45: aload_2
46: invokevirtual #39 // Method java/text/SimpleDateFormat.setTimeZone:(Ljava/util/TimeZone;)V
49: return
LineNumberTable:
line 52: 0
line 53: 4
line 54: 16
line 55: 24
line 56: 29
line 57: 41
line 58: 49
public org.mortbay.util.ajax.JSONDateConvertor(java.lang.String, java.util.TimeZone, boolean, java.util.Locale);
descriptor: (Ljava/lang/String;Ljava/util/TimeZone;ZLjava/util/Locale;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=7, locals=5, args_size=5
0: aload_0
1: invokespecial #20 // Method java/lang/Object."<init>":()V
4: aload_0
5: new #22 // class org/mortbay/util/DateCache
8: dup
9: aload_1
10: aload 4
12: invokespecial #43 // Method org/mortbay/util/DateCache."<init>":(Ljava/lang/String;Ljava/util/Locale;)V
15: putfield #27 // Field _dateCache:Lorg/mortbay/util/DateCache;
18: aload_0
19: getfield #27 // Field _dateCache:Lorg/mortbay/util/DateCache;
22: aload_2
23: invokevirtual #31 // Method org/mortbay/util/DateCache.setTimeZone:(Ljava/util/TimeZone;)V
26: aload_0
27: iload_3
28: putfield #33 // Field _fromJSON:Z
31: aload_0
32: new #35 // class java/text/SimpleDateFormat
35: dup
36: aload_1
37: new #45 // class java/text/DateFormatSymbols
40: dup
41: aload 4
43: invokespecial #48 // Method java/text/DateFormatSymbols."<init>":(Ljava/util/Locale;)V
46: invokespecial #51 // Method java/text/SimpleDateFormat."<init>":(Ljava/lang/String;Ljava/text/DateFormatSymbols;)V
49: putfield #38 // Field _format:Ljava/text/SimpleDateFormat;
52: aload_0
53: getfield #38 // Field _format:Ljava/text/SimpleDateFormat;
56: aload_2
57: invokevirtual #39 // Method java/text/SimpleDateFormat.setTimeZone:(Ljava/util/TimeZone;)V
60: return
LineNumberTable:
line 61: 0
line 62: 4
line 63: 18
line 64: 26
line 65: 31
line 66: 52
line 67: 60
public org.mortbay.util.ajax.JSONDateConvertor(boolean);
descriptor: (Z)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
0: aload_0
1: getstatic #55 // Field org/mortbay/util/DateCache.DEFAULT_FORMAT:Ljava/lang/String;
4: ldc #57 // String GMT
6: invokestatic #63 // Method java/util/TimeZone.getTimeZone:(Ljava/lang/String;)Ljava/util/TimeZone;
9: iload_1
10: invokespecial #65 // Method "<init>":(Ljava/lang/String;Ljava/util/TimeZone;Z)V
13: return
LineNumberTable:
line 48: 0
line 49: 13
public java.lang.Object fromJSON(java.util.Map);
descriptor: (Ljava/util/Map;)Ljava/lang/Object;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=3, args_size=2
0: aload_0
1: getfield #33 // Field _fromJSON:Z
4: ifne 15
7: new #69 // class java/lang/UnsupportedOperationException
10: dup
11: invokespecial #70 // Method java/lang/UnsupportedOperationException."<init>":()V
14: athrow
15: aload_0
16: getfield #38 // Field _format:Ljava/text/SimpleDateFormat;
19: astore_2
20: aload_2
21: monitorenter
22: aload_0
23: getfield #38 // Field _format:Ljava/text/SimpleDateFormat;
26: aload_1
27: ldc #72 // String value
29: invokeinterface #78, 2 // InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
34: checkcast #80 // class java/lang/String
37: invokevirtual #84 // Method java/text/SimpleDateFormat.parseObject:(Ljava/lang/String;)Ljava/lang/Object;
40: astore_1
41: aload_2
42: monitorexit
43: aload_1
44: areturn
45: astore_1
46: aload_2
47: monitorexit
48: aload_1
49: athrow
50: astore_1
51: aload_1
52: invokestatic #90 // Method org/mortbay/log/Log.warn:(Ljava/lang/Throwable;)V
55: aconst_null
56: astore_1
57: goto 43
Exception table:
from to target type
15 22 50 Class java/lang/Exception
22 43 45 any
46 48 45 any
48 50 50 Class java/lang/Exception
LineNumberTable:
line 71: 0
line 72: 7
line 75: 15
line 77: 22
line 84: 43
line 78: 45
line 80: 50
line 82: 51
line 84: 55
public void toJSON(java.lang.Object, org.mortbay.util.ajax.JSON$Output);
descriptor: (Ljava/lang/Object;Lorg/mortbay/util/ajax/JSON$Output;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=4, args_size=3
0: aload_0
1: getfield #27 // Field _dateCache:Lorg/mortbay/util/DateCache;
4: aload_1
5: checkcast #96 // class java/util/Date
8: invokevirtual #100 // Method org/mortbay/util/DateCache.format:(Ljava/util/Date;)Ljava/lang/String;
11: astore_3
12: aload_0
13: getfield #33 // Field _fromJSON:Z
16: ifeq 39
19: aload_2
20: aload_1
21: invokevirtual #104 // Method java/lang/Object.getClass:()Ljava/lang/Class;
24: invokeinterface #110, 2 // InterfaceMethod org/mortbay/util/ajax/JSON$Output.addClass:(Ljava/lang/Class;)V
29: aload_2
30: ldc #72 // String value
32: aload_3
33: invokeinterface #114, 3 // InterfaceMethod org/mortbay/util/ajax/JSON$Output.add:(Ljava/lang/String;Ljava/lang/Object;)V
38: return
39: aload_2
40: aload_3
41: invokeinterface #117, 2 // InterfaceMethod org/mortbay/util/ajax/JSON$Output.add:(Ljava/lang/Object;)V
46: goto 38
LineNumberTable:
line 89: 0
line 90: 12
line 92: 19
line 93: 29
line 99: 38
line 97: 39
}
```
</details>
### Steps to Reproduce
<!---
Provide a self-contained example as an attached archive. If the source code to
reproduce the issue is very small you can include it here directly (with its
inputs, most likely some binary .class files, as an attached archive).
-->
Use the [following class](/uploads/1ca6f57e852e1533a5a2e28016fb5b1a/JSONDateConvertor.class) and analyze it using:
```java
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.BasicValue;
import org.objectweb.asm.tree.analysis.BasicVerifier;
import org.objectweb.asm.util.CheckClassAdapter;
public class Test {
public static void main(String[] args) throws Throwable {
ClassReader cr = new ClassReader(Files.newInputStream(Paths.get("JSONDateConvertor.class")));
ClassNode cn = new ClassNode();
cr.accept(new CheckClassAdapter(cn, false), ClassReader.SKIP_DEBUG);
List<MethodNode> methods = cn.methods;
for (MethodNode method : methods) {
BasicVerifier verifier = new BasicVerifier();
Analyzer<BasicValue> a = new Analyzer<>(verifier);
a.analyze(cn.name, method);
}
}
}
```
This yields the following exception:
```
Exception in thread "main" org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 30: Expected an object reference, but found .
at org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:275)
at Test.main(Test.java:26)
Caused by: org.objectweb.asm.tree.analysis.AnalyzerException: Expected an object reference, but found .
at org.objectweb.asm.tree.analysis.BasicVerifier.copyOperation(BasicVerifier.java:90)
at org.objectweb.asm.tree.analysis.BasicVerifier.copyOperation(BasicVerifier.java:44)
at org.objectweb.asm.tree.analysis.Frame.execute(Frame.java:326)
at org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:171)
... 1 more
```https://gitlab.ow2.org/asm/asm/-/issues/318008ClassRemapper fails to transform Calcite 1.36.0 SqlFunctions2024-01-26T17:34:43ZGuillaume MasséClassRemapper fails to transform Calcite 1.36.0 SqlFunctionsI'm sorry I can't minimize this issue further, I'm using asm via sbt-assembly to perform class shading on some of our libraries. When upgrading from calcite 1.35.0 to 1.36.0.
Good: https://github.com/apache/calcite/blob/calcite-1.35.0/c...I'm sorry I can't minimize this issue further, I'm using asm via sbt-assembly to perform class shading on some of our libraries. When upgrading from calcite 1.35.0 to 1.36.0.
Good: https://github.com/apache/calcite/blob/calcite-1.35.0/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
Bad: https://github.com/apache/calcite/blob/calcite-1.36.0/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
I noticed that class shading failed with:
```
java.lang.ArrayIndexOutOfBoundsException: Index 65536 out of bounds for length 297
at org.objectweb.asm.ClassReader.readLabel(ClassReader.java:2695)
at org.objectweb.asm.ClassReader.createLabel(ClassReader.java:2711)
```
I first reproduced the issue with Scala's fork of jarjar: https://github.com/eed3si9n/jarjar-abrams/pull/45
But then I noticed that it's most likely a asm bug and made more minimization to limit myself to asm: https://github.com/MasseGuillaume/asm-remapper-bug
In the issue template, you mention `CheckClassAdapter`, I gave it a shot and it's indeed an error with labels:
```
java.lang.IllegalArgumentException: Invalid end label (must be visited first)
at org.objectweb.asm.util.CheckMethodAdapter.checkLabel(CheckMethodAdapter.java:1453)
at org.objectweb.asm.util.CheckMethodAdapter.visitLocalVariableAnnotation(CheckMethodAdapter.java:996)
at org.objectweb.asm.MethodVisitor.visitLocalVariableAnnotation(MethodVisitor.java:757)
at org.objectweb.asm.commons.MethodRemapper.visitLocalVariableAnnotation(MethodRemapper.java:257)
at org.objectweb.asm.ClassReader.readCode(ClassReader.java:2614)
at org.objectweb.asm.ClassReader.readMethod(ClassReader.java:1515)
at org.objectweb.asm.ClassReader.accept(ClassReader.java:745)
at org.objectweb.asm.ClassReader.accept(ClassReader.java:425)
at remapper.bug.RemapperTest.remapper(RemapperTest.java:42)
```
I don't think it's because of how I use asm (maybe I'm wrong):
```java
byte[] bytecode = Files.readAllBytes(Paths.get("../SqlFunctions.class"));
Remapper remapper = new Remapper() {};
ClassReader classReader = new ClassReader(bytecode);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor classVisitor = new CheckClassAdapter(classWriter);
ClassRemapper classRemapper = new ClassRemapper(classVisitor, remapper);
classReader.accept(classRemapper, ClassReader.EXPAND_FRAMES);
byte[] outputBytecode = classWriter.toByteArray();
ClassReader classReader2 = new ClassReader(outputBytecode);
ClassWriter classWriter2 = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor visitor = new ClassVisitor(Opcodes.ASM9, classWriter2) {};
classReader2.accept(visitor, ClassReader.EXPAND_FRAMES);
```https://gitlab.ow2.org/asm/asm/-/issues/318007`ClassNode#outerClass` has incorrect JavaDocs.2023-11-18T13:54:41ZMarcus Sawyer`ClassNode#outerClass` has incorrect JavaDocs.See title. The docs read:
```java
/**
* The internal name of the enclosing class of this class (see {@link
* org.objectweb.asm.Type#getInternalName()}). Must be {@literal null} if this class has no
* enclosing class, or if it ...See title. The docs read:
```java
/**
* The internal name of the enclosing class of this class (see {@link
* org.objectweb.asm.Type#getInternalName()}). Must be {@literal null} if this class has no
* enclosing class, or if it is a local or anonymous class.
*/
```
whereas in fact, the attribute is *only* populated for anonymous and local classes. It seems to have been erroneously copied from [this part of the spec](https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.7.6:~:text=representing%20C.-,outer_class_info_index,-If%20C%20is), which is in fact talking about the `InnerClasses` attribute.
The correct section is [here](https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.7.7:~:text=must%20be%20four.-,class_index,-The%20value%20of), since this field is actually populated by the (slightly poorly-named) `visitOuterClass` method based on the `enclosingMethodOffset` in `ClassReader`.https://gitlab.ow2.org/asm/asm/-/issues/318006asm-bom packaging should be "pom"2023-10-28T18:35:20ZAdrien Bernardasm-bom packaging should be "pom"ASM produces a BOM file to help at managing dependencies: asm-bom. But the generated pom file has no 'packaging' tag, making it default to "jar". There is no jar for a BOM file, the best practice would be to use a "pom" packaging tag.
N...ASM produces a BOM file to help at managing dependencies: asm-bom. But the generated pom file has no 'packaging' tag, making it default to "jar". There is no jar for a BOM file, the best practice would be to use a "pom" packaging tag.
Note that I didn't find this causing any issue when using maven (and even less in maven). I found it because of an error in our custom toolchain to review third party software. Because of the jar packaging we would try to download the jar file which does not exist. It's a really minor point we worked-around it qucikly. I just figured I would report this as it would be cleaner to use "pom" here.
This is present in asm 9.6.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/318004ASM library is running into an internal error2023-10-21T12:39:52ZPavlo DorokhovASM library is running into an internal error### Expected Behavior
No exception when ASM library load "zzZOF.class" class file
### Current Behavior
Method "zzZ" of class "zzZOF" of the "aspose-words-15.8.0.jar" has an unusual structure within it, that doesn't prevent execution by ...### Expected Behavior
No exception when ASM library load "zzZOF.class" class file
### Current Behavior
Method "zzZ" of class "zzZOF" of the "aspose-words-15.8.0.jar" has an unusual structure within it, that doesn't prevent execution by the JVM, but the library that used class files experiences an exception.
### Steps to Reproduce
Jar file from "http://develop.centit.com/nexus/content/repositories/centit-releases/com/aspose/aspose-words/15.8.0/aspose-words-15.8.0-jdk16.jar".
Below is a raw reproduction of the issue using the most recent 9.5 version of this "asm" java bytecode reader library:
ifn="/Users/pavel/Downloads/aspose-words-15.8.0-jdk16/com/aspose/words/zzZOF.class";echo 'String ifn=System.getProperty("ifn");FileInputStream fis=new FileInputStream(new File(ifn));import org.objectweb.asm.ClassReader;import org.objectweb.asm.tree.ClassNode;import org.objectweb.asm.ClassVisitor;ClassNode clazz=new ClassNode();ClassReader reader=new ClassReader(fis);reader.accept((ClassVisitor)clazz, 0);fis.close();' | /Users/pavel/Documents/jre/bin/jshell -R-Difn="$ifn" -c /Users/pavel/Downloads/asm-9.5.jar:/Users/pavel/Downloads/asm-tree-9.5.jar -
Exception java.lang.ArrayIndexOutOfBoundsException: Index 107 out of bounds for length 99
at ClassReader.readLabel (ClassReader.java:2694)
at ClassReader.createLabel (ClassReader.java:2710)
at ClassReader.readStackMapFrame (ClassReader.java:3369)
at ClassReader.readCode (ClassReader.java:2089)
at ClassReader.readMethod (ClassReader.java:1514)
at ClassReader.accept (ClassReader.java:744)
at ClassReader.accept (ClassReader.java:424)
at (#8:1)
If you have any questions please contact pashadtvwork@gmail.comhttps://gitlab.ow2.org/asm/asm/-/issues/318003The Textifier prints a supplementary space at the end of each method that thr...2023-10-21T12:40:25ZRemi ForaxThe Textifier prints a supplementary space at the end of each method that throws at least one exceptionAs said in the title, the Textifier prints a supplementary space at the end of each method that throws at least one exception.As said in the title, the Textifier prints a supplementary space at the end of each method that throws at least one exception.Remi ForaxRemi Foraxhttps://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/318000java.lang.IllegalArgumentException in SignatureReader for method return an Op...2023-05-12T16:01:29ZMaximilian Ostjava.lang.IllegalArgumentException in SignatureReader for method return an Optional StreamFollowing exception stack trace
>
> java.lang.IllegalArgumentException
> at org.objectweb.asm.signature.SignatureReader.parseType(SignatureReader.java:249)
> at org.objectweb.asm.signature.SignatureReader.accept(SignatureReader.java:...Following exception stack trace
>
> java.lang.IllegalArgumentException
> at org.objectweb.asm.signature.SignatureReader.parseType(SignatureReader.java:249)
> at org.objectweb.asm.signature.SignatureReader.accept(SignatureReader.java:114)
> at org.apache.maven.shared.dependency.analyzer.asm.DefaultClassVisitor.addSignature(DefaultClassVisitor.java:153)
> at org.apache.maven.shared.dependency.analyzer.asm.DefaultClassVisitor.visitMethod(DefaultClassVisitor.java:133)
> at org.objectweb.asm.ClassReader.readMethod(ClassReader.java:1353)
> at org.objectweb.asm.ClassReader.accept(ClassReader.java:744)
> at org.objectweb.asm.ClassReader.accept(ClassReader.java:424)
> at org.apache.maven.shared.dependency.analyzer.asm.DependencyClassFileVisitor.visitClass(DependencyClassFileVisitor.java:71)
> at org.apache.maven.shared.dependency.analyzer.asm.ResultCollectorTest.getDependencies(ResultCollectorTest.java:38)
> at org.apache.maven.shared.dependency.analyzer.asm.ResultCollectorTest.testClassWithStreamAndOptional(ResultCollectorTest.java:69)
is thrown for class
`
package pkg;
import java.io.Serializable;
import java.util.Optional;
import java.util.stream.Stream;
public class CUnderTest {
public Stream<Serializable> doMyStuff() {
Stream<Serializable> s = Stream.empty();
return Optional.ofNullable(s).orElseGet(Stream::of);
}
}
`
### Expected Behavior
no exception is thrown and signature for '()+Ljava/util/stream/Stream<Ljava/io/Serializable;>;' parsing works
### Current Behavior
IllegalArgumentException is thrown. The signature '()+Ljava/util/stream/Stream<Ljava/io/Serializable;>;' contains a + which is not handled correctly in the method org.objectweb.asm.signature.SignatureReader.parseType(String, int, SignatureVisitor)
### Steps to Reproduce
Extend the test class in
org.apache.maven.shared.dependency.analyzer.asm.ResultCollectorTest
of
https://github.com/apache/maven-dependency-analyzer.git
with following test:
`
@Test
public void testClassWithStreamAndOptional() throws IOException {
Set<String> dependencies = getDependencies(CUnderTest.class);
assertThat(dependencies).contains("java.util.stream.Stream");
}
`https://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/317996Javadoc Search is Broken2023-04-15T11:43:14ZthekiefJavadoc Search is Broken<!---
Before creating a new issue, consider the following (this does not apply to
feature requests):
- if you need help on how to use ASM, please consult the documentation first
(https://asm.ow2.io/documentation.html). If you can't find...<!---
Before creating a new issue, consider the following (this does not apply to
feature requests):
- if you need help on how to use ASM, please consult the documentation first
(https://asm.ow2.io/documentation.html). If you can't find the answer there,
describe your problem on the http://mail.ow2.org/wws/info/asm mailing listing
instead of using GitLab issues. The mailing list has more subscribers than the
GitLab issue tracker, and you will thus likely get a quicker answer via the
mailing list.
- check if your *input* classes are valid. For this you can use "javap -p -c -v
<yourclass.class>". If this command fails, the bug is most likely in the tool
that produced this class, not in ASM. Check the javap output carefully:
sometimes the tool is able to print most of the class content, but fails to
parse some part of it. In this case there can be an error message in the middle
of the javap output, which can be easy to miss at first sight.
- check if you are using the ASM API correctly. For this, insert one or more
CheckClassAdapter instances in your class generation or class transformation
chain (in particular, in front of ClassWriter instances). If a CheckClassAdapter
throws an exception, this means that you are not using the ASM API correctly.
Fix these issues first.
If you still have a bug after completing the above steps, describe it below and
make sure to provide detailed instructions on how to reproduce it.
-->
<!--- Provide a brief summary of the issue in the title above -->
### Expected Behavior
<!--- Tell us what should happen. -->
Whe searching for a term such as ALOAD and clicking on the entry following site should open:
https://asm.ow2.io/javadoc/org/objectweb/asm/Opcodes.html#ALOAD
### Current Behavior
<!--- Tell us what happens instead of the expected behavior. -->
Clicking on the entry forwards to
https://asm.ow2.io/javadoc/undefined/org/objectweb/asm/Opcodes.html#ALOAD
### Steps to Reproduce
<!---
Provide a self-contained example as an attached archive. If the source code to
reproduce the issue is very small you can include it here directly (with its
inputs, most likely some binary .class files, as an attached archive).
-->
* Open the Javadoc and use the search field.
* Enter a term and click on a result.https://gitlab.ow2.org/asm/asm/-/issues/317995Invalid stackmap generated when the instruction stream has new instruction af...2023-06-26T17:36:57ZSøren GjesseInvalid stackmap generated when the instruction stream has new instruction after invokespecial to <init>Using the ASM code below generates a class file which fails on the JVM with:
```
StackMapTable format error: bad offset for Uninitialized in method Test.main([Ljava/lang/String;)V
```
The javap dump of the generated `main` method is:
```...Using the ASM code below generates a class file which fails on the JVM with:
```
StackMapTable format error: bad offset for Uninitialized in method Test.main([Ljava/lang/String;)V
```
The javap dump of the generated `main` method is:
```
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 #16 // Field java/lang/System.out:Ljava/io/PrintStream;
3: goto 12
6: invokespecial #21 // Method "<init>":()V
9: goto 19
12: new #2 // class Test
15: dup
16: goto 6
19: invokevirtual #25 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
22: return
StackMapTable: number_of_entries = 4
frame_type = 255 /* full_frame */
offset_delta = 6
locals = [ class "[Ljava/lang/String;" ]
stack = [ class java/io/PrintStream, uninitialized 0, uninitialized 0 ]
frame_type = 255 /* full_frame */
offset_delta = 2
locals = [ class "[Ljava/lang/String;" ]
stack = [ class java/io/PrintStream, class Test ]
frame_type = 255 /* full_frame */
offset_delta = 2
locals = [ class "[Ljava/lang/String;" ]
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 6
locals = [ class "[Ljava/lang/String;" ]
stack = [ class java/io/PrintStream, class Test ]
```
The issue are the two `uninitialized 0` in the stackmap. The code has the `new` instruction later in the instruction stream than the `invokespecial` to `<init>`.
Code to generate the class file:
```
public static byte[] dump() throws Exception {
ClassWriter classWriter = new ClassWriter(0);
MethodVisitor methodVisitor;
classWriter.visit(V1_8, ACC_FINAL | ACC_SUPER, "Test", null, "java/lang/Object", null);
{
methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(1, 1);
methodVisitor.visitEnd();
}
{
Label labelNew = new Label();
Label labelInit = new Label();
Label labelAfterInit = new Label();
methodVisitor =
classWriter.visitMethod(
ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
methodVisitor.visitCode();
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitJumpInsn(GOTO, labelNew);
methodVisitor.visitLabel(labelInit);
methodVisitor.visitFrame(
Opcodes.F_FULL,
1,
new Object[] {"[Ljava/lang/String;"},
3,
new Object[] {"java/io/PrintStream", labelNew, labelNew});
methodVisitor.visitMethodInsn(INVOKESPECIAL, MAIN_CLASS, "<init>", "()V", false);
methodVisitor.visitFrame(
Opcodes.F_FULL,
1,
new Object[] {"[Ljava/lang/String;"},
2,
new Object[] {"java/io/PrintStream", MAIN_CLASS});
methodVisitor.visitJumpInsn(GOTO, labelAfterInit);
methodVisitor.visitLabel(labelNew);
methodVisitor.visitFrame(
Opcodes.F_FULL,
1,
new Object[] {"[Ljava/lang/String;"},
1,
new Object[] {"java/io/PrintStream"});
methodVisitor.visitTypeInsn(NEW, MAIN_CLASS);
methodVisitor.visitInsn(DUP);
methodVisitor.visitJumpInsn(GOTO, labelInit);
methodVisitor.visitLabel(labelAfterInit);
methodVisitor.visitFrame(
Opcodes.F_FULL,
1,
new Object[] {"[Ljava/lang/String;"},
2,
new Object[] {"java/io/PrintStream", MAIN_CLASS});
methodVisitor.visitMethodInsn(
INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(3, 1);
methodVisitor.visitEnd();
}
{
methodVisitor =
classWriter.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
methodVisitor.visitCode();
methodVisitor.visitLdcInsn("Hello, world!");
methodVisitor.visitInsn(ARETURN);
methodVisitor.visitMaxs(1, 1);
methodVisitor.visitEnd();
}
classWriter.visitEnd();
return classWriter.toByteArray();
}
```
To work around the issue for my concrete application I added patching of the bytes from ASM using javassist (see [here](https://r8.googlesource.com/r8/+/9baf2a528d42b1b238605f73568d589b0b32fb83/src/test/java/com/android/tools/r8/cf/frames/InitBeforeNewInInstructionStreamTest.java#86))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/317992Compiled wildcard is not correctly handled by ASM2023-05-10T16:00:56ZKrystian KosekCompiled wildcard is not correctly handled by ASMI have a fragment of code:
`.findFirst().orElseThrow(() -> new Error("Code not found"))`
Which after compiling with use of jdk8 with tycho-maven-plugin 0.22 generates below code:
`Signature ()+Ljava/lang/Error; ~ java/lang/Error € Co...I have a fragment of code:
`.findFirst().orElseThrow(() -> new Error("Code not found"))`
Which after compiling with use of jdk8 with tycho-maven-plugin 0.22 generates below code:
`Signature ()+Ljava/lang/Error; ~ java/lang/Error € Code not found`
When I pass the compiled code through ASM, SignatureReader class throws an IllegalArgumentException when it encounters ‘+’ character.
I've tested this on several asm versions and I see that versions below 6.1 pass this while newer versions throw this exception.
(Compiling code without tycho or with newer tycho version doesn't generate this `Signature� �()+Ljava/lang/Error;� ` part and generated code passes through asm, but for internal reason I am unable to upgrade tycho to a newer version at the moment)
Whereas javap command read generated .class file correctly without informing about any error. On the contrary jd-gui is not able to decompile this .class file.
I assume that if javap doesn't report any error, it's a valid java bytecode.
File compiled with tycho 0.22: [TestClass.class](/uploads/23b75f061a1928f12f5c5f7a4f778699/TestClass.class)https://gitlab.ow2.org/asm/asm/-/issues/317991Analyzer produces frames that have different locals than those detected by JR...2023-12-21T12:17:45ZMiljenko BrkicAnalyzer produces frames that have different locals than those detected by JRE bytecode verifier
Issue was detected with class file instrumented by [Quasar](http://docs.paralleluniverse.co/quasar/) which uses ASM for instrumentation. QuasarInstrumentor instruments suspendable methods to preserve/restore Fiber stack. `Analyzer.analy...
Issue was detected with class file instrumented by [Quasar](http://docs.paralleluniverse.co/quasar/) which uses ASM for instrumentation. QuasarInstrumentor instruments suspendable methods to preserve/restore Fiber stack. `Analyzer.analyze()` is used to get Frame data based on which it determines variables that need to be pushed on the Fiber stack. However, bytecode verification of generated class fails with:
```
java.lang.VerifyError: Bad local variable type
Exception Details:
Location:
net/cordapp/utxo/apples/flows/pack/PackApplesFlow.call(Lnet/corda/v5/application/flows/ClientRequestBody;)Ljava/lang/String; @1508: iload
Reason:
Type top (current frame, locals[8]) is not assignable to integer
Current Frame:
bci: @1508
flags: { }
locals: { 'net/cordapp/utxo/apples/flows/pack/PackApplesFlow', 'net/corda/v5/application/flows/ClientRequestBody', 'net/corda/v5/membership/NotaryInfo', 'java/security/PublicKey', 'net/corda/v5/ledger/common/Party', 'net/corda/v5/ledger/common/Party', 'net/cordapp/utxo/apples/states/BasketOfApples', 'net/corda/v5/ledger/utxo/transaction/UtxoSignedTransaction', top, 'java/lang/Exception', top, top, top, 'co/paralleluniverse/fibers/suspend/StackOps', integer, null }
stack: { }
```
According to ASM Analyzer, local[8] is "I", while according to bytecode verifier it is "top".
### Expected Behavior
Analyzer produces frames that have the same locals as detected by bytecode verifier.
### Current Behavior
Analyzer produces frames that have different locals than detected by bytecode verifier which leads to VerifyError after instructions are added that handle such locals (in this case Quasar adds instructions that push all local variables on Fibre stack).
### Steps to Reproduce
Provided generated class and unit tests that show the issue: [asm.zip](/uploads/ac5fec819f52deca1b32ad0be0729726/asm.zip)
- Test `loadingClassThrowsVerifyError` reproduces `VerifyError` for generated class when class is verified by bytecode verifier.
- Test `verifyingClassWithAsmDoesNotFindErrors` shows that ASM verifier doesn't find any issue with generated class.
- Test `asmCalculatedFrameHasDifferentValue` shows that ASM produces frame which has local[8] set to "I", while bytecode verifier finds it's "top" (shown in error message from `loadingClassThrowsVerifyError`)