judovana / java-runtime-decompiler

GNU General Public License v3.0
68 stars 14 forks source link

error on compile #251

Open sherkat69 opened 1 year ago

sherkat69 commented 1 year ago

i built a executable test jar with jdk 17 java_home is also set

i run my test app and attach the JRD to it. when i try to recompile i get: java.lang.IllegalArgumentException: Unsupported class file major version 63 Attempt to compile failed with - java.lang.IllegalArgumentException: Unsupported class file major version 63, you may close dialog

any tips on how to fix this?!

judovana commented 1 year ago

Hello 63 is java 19. So the class you are attempting to compile back to running jvm, is compield by jdk19( - or have some jdk19 depndence?). Your jrd seesmto be runnign in older JDK, so it is unabel to process jdk 19 bytecode.

The straightforward fix shoudl be t run JRD in jdk19. The JRD can allow you to set exact bytecode version to compile to, but if you are going to hotswap the class, it will fai.

What JRD are you using? On what os? java_home will not work, but JAVA_HOME will.

Thanx!

judovana commented 1 year ago

Hello! Have it helped? Maybe worthy to highlight, taht wwe are speakig about three JVMs here. JVM where application runs, JVM which runs j-r-d, and the JVM which created application. Obviously, the JVM which built appliaction and JVM which run it, are comaptible, otherwise yo would immediately know. But JRD is an alien, which runs in its JVM, and which injects to the running jvm. This is quite tricky to keep sane.

Main number in this process is bytecode level version. During build, you can affect the output bytecode by -source/-target javac swithces. If not used, the bytecode level of compiling JVM is used

If JRD is operating in foreign JVM (where your application is running) it always checks the bytecode level of classes it works with. And is enforcing it. You can manually override it, but if you are njecting freshly compiled code to the running vm, it is bad idea - if class bytecode level is different then original, hotswap fails.

Now consier, that both JRD's JVM and target application JVM must understand the bytecode it operates. Usually the process is that JRD is run in same or newer JVM then target. If JRD is running in older VM, it can obtain bytecode it do not understand, or it can not compile into, which seems to be case you are hitting

sherkat69 commented 1 year ago

no i still have some problem. i compile both targets with jdk 17 fastdebug then run the JRD with jdk 19

  1. i compile this sample and i get error when trying to recompile
    
    import java.awt.BorderLayout;
    import java.awt.FlowLayout;
    import java.awt.LayoutManager;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;

import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import java.awt.Color;

public class SwingTester { public static void main(String[] args) {

  createWindow();

}

private static void createWindow() {
JFrame frame = new JFrame("Swing Tester"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  createUI(frame);
  frame.setSize(560, 200);      
  frame.setLocationRelativeTo(null);  
  frame.setVisible(true);

}

private static void myMethod(String fname) {
    System.out.println(fname + " Refsnes");
}

private static void createUI(final JFrame frame){
JPanel panel = new JPanel(); LayoutManager layout = new FlowLayout();
panel.setLayout(layout);
JButton button = new JButton("Click Me!");

  button.addActionListener(new ActionListener() {
     @Override
     public void actionPerformed(ActionEvent e) {
        frame.getContentPane().setBackground(Color.pink);
        JOptionPane.showMessageDialog(frame, "Welcome to Swing!");

        int x = 1;
        int y = 4;

        JOptionPane.showMessageDialog(frame, "1 + 4 is: "+(x+y));

        myMethod("XXX");
myMethod("YYY");
myMethod("ZZZ");

     }
  });

  panel.add(button);
  frame.getContentPane().add(panel, BorderLayout.CENTER);    

}
}


 error:

java.lang.IllegalArgumentException: Unsupported class file major version 63 Attempt to compile failed with - java.lang.IllegalArgumentException: Unsupported class file major version 63, you may close dialog


2. i recompile another sample with the very same settings. it recompiles but i dont see any changes:

class HelloWorld {
public static void main(String[] args) {
//Using no condition in for loop
for(;;){
System.out.println("infinitive loop"); wait(1000);
}
}

public static void wait(int ms) { try { Thread.sleep(ms); } catch(InterruptedException ex) { Thread.currentThread().interrupt(); } }

}

judovana commented 1 year ago
 a=`mktemp -d`
 cd $a
wget https://github.com/pmikova/java-runtime-decompiler/releases/download/7.0-snapshot.9/runtime-decompiler-7.0-SNAPSHOT.9-with-decompilers.tar.xz
 tar -xf runtime-decompiler-7.0-SNAPSHOT.9-with-decompilers.tar.xz 
 vim SwingTester.java
 /usr/lib/jvm/java-17-openjdk/bin/javac  -cp . SwingTester.java
/usr/lib/jvm/java-17-openjdk/bin/java  -cp . SwingTester &
 JAVA_HOME=/usr/lib/jvm/java-19-openjdk runtime-decompiler-7.0-SNAPSHOT.9-with-decompilers/start.sh

Now, note, that this is clean "install" using config from untarred location, which may meter. In this moment I can see two windows - yours SwingTester, and JRD. JRD is showng two java processes JRD itself, and SwingTester. Clck on SwingTester. First class on top is SwingTester. Clicking it. I see proeprly decompiled code by CFR which is should be default plugin (and best decompiler) I click compile (arrow down, next to arrow up next to plugins). It compile fine. I click compile and upload (arrow up, next to plugins). It compile, but upload correctly fails:

java.lang.Exception: Agent returned error: error java.lang.RuntimeException: Agent returned error response 'java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)' for request 'OVERWRITE\nSwingTester$1\nyv66vgAAAD0A...

Which is conencted to the inner class in decompiled file, to the method createUI. But that is different topic. For simpolicity, lets replace Source buffer of freshly decompiled class by by the original SwingTester.java contnet (copy+paste). I click compile and upload (arrow up, next to plugins). It compile, and upload properly.

Attempt to compile finished successfully, you may close dialog
[2022-12-31T12:04:44.734Z] uploaded: SwingTester$1
[2022-12-31T12:04:44.748Z] uploaded: SwingTester

If I edit this code, and comple and upload, the changes are visible. Note, that the changes takes effect, onloy if JVM reads the redefinition of the class (which nearly always, eg for sure when new is called).

So in this example, if I change String in JFrame frame = new JFrame("Swing Tester"); or text in button, it will have no effect. But changing JOptionPane.showMessageDialog(frame, "Welcome to Swing!"); will always take effect. Changing System.out.println(fname + " Refsnes"); will have effect nearly always - unless method is JITed (then it willt ake effect only after it is remvoed from JITed methds). This is answering yours second question. If you move the println to different method or to inner class and call it with new, you will see the change. Already now, in your exmaple, if yo change the Thread.sleep(n); to eg Thread.sleep(100); you will have visible difference.

Now very major hint. When I was clicking SwingTester or compile or compile and upload buttons, I could clearly see button with BOLD 17 - this button may be hidden on small monitors or even during default startup under some resolutions. Do you see it? If not, pelase maximize the JRD window (I will need to fix this it seems, PR welcomed :) ). The 17 shows the bytecode level of freshly decompiled class. If the text is bold (and tooltip contains force: true) then the file will be compiled to this level of bytecode. If it is not bold then default (JRD's JVM one) level, or what is written in compiler arguments in Settings (--target) will be used. This very important thing should bes true by default. It is controlled in Configure->Settings->Override source/target per class. IfI I unselect it now , so the freshly decompiled SwingTester 's 17 will no longer be bold, and click compile and upload then I got

java.lang.Exception: Agent returned error: error java.lang.RuntimeException: Agent returned error response 'java.lang.UnsupportedClassVersionError' for request 'OVERWRITE\nSwingTester\nyv66vg...

This is what I guess happened to you. You can fix it by selecting the Override source/target per class in settings, or by setting --target in compiler arguments. The first is better.

Two questions to you remain:

I will be happy to help there, But I'm missing some crucial part somwhere.

judovana commented 1 year ago

Just for record the second exmaple:

 vim HelloWorld.java
/usr/lib/jvm/java-17-openjdk/bin/javac HelloWorld.java 
 /usr/lib/jvm/java-17-openjdk/bin/java HelloWorld &
 JAVA_HOME=/usr/lib/jvm/java-19-openjdk runtime-decompiler-7.0-SNAPSHOT.9-with-decompilers/start.sh

Yet again, I see JRD's window with HelloWorld process, which after click contains HelloWorld class, which shows bold 17 when clicked, and which can be edited and compiel dand uploaded right from the box. In cli, it would be:

JAVA_HOME=/usr/lib/jvm/java-19-openjdk runtime-decompiler-7.0-SNAPSHOT.9-with-decompilers/start.sh  --listjvms
13919 org.jrd.backend.data.Main --listjvms
13750 HelloWorld
JAVA_HOME=/usr/lib/jvm/java-19-openjdk runtime-decompiler-7.0-SNAPSHOT.9-with-decompilers/start.sh  --decompile 13750 Cfr  HelloWorld > blah
Processing HelloWorld

...edit, compile (JRD's cli compiler have issues with default package (yet again something to fix I guess...)

JAVA_HOME=/usr/lib/jvm/java-19-openjdk runtime-decompiler-7.0-SNAPSHOT.9-with-decompilers/start.sh  --overwrite 13750 HelloWorld < HelloWorld.class 
Overwrite of class 'HelloWorld' successful.

Yet again, If I unselect the Override source/target per class in Settngs both cli and gui will fail with

java.lang.RuntimeException: Agent returned error response 'java.lang.UnsupportedClassVersionError' for request 'OVERWRITE\nHelloWorld

hth

judovana commented 1 year ago

I realized I' may be on wrong track. You have already compile issue with wrong bytecode. I was able to reproduce your failure by swapping the compile+runtime with jrd's runtimes:

 a=`mktemp -d`
 cd $a
wget https://github.com/pmikova/java-runtime-decompiler/releases/download/7.0-snapshot.9/runtime-decompiler-7.0-SNAPSHOT.9-with-decompilers.tar.xz
 tar -xf runtime-decompiler-7.0-SNAPSHOT.9-with-decompilers.tar.xz 
 vim SwingTester.java
 /usr/lib/jvm/java-19-openjdk/bin/javac  -cp . SwingTester.java   # was 17 is now 19
/usr/lib/jvm/java-19-openjdk/bin/java  -cp . SwingTester &  # was is now 19
 JAVA_HOME=/usr/lib/jvm/java-17-openjdk runtime-decompiler-7.0-SNAPSHOT.9-with-decompilers/start.sh #was 19 is now 17

In that case I got java.lang.IllegalArgumentException: Unsupported class file major version 63 for already for decompialtion and

java.lang.IllegalArgumentException: Unsupported class file major version 63
Attempt to compile failed with - java.lang.IllegalArgumentException: Unsupported class file major version 63, you may close dialog

for compilation, where I had to provide code, as there was nothing decompiled. Can you confirm?

sherkat69 commented 1 year ago

i really appreciate your sincere attention to my problem. unfortunately, i cant reach my working environment to test your suggestions. after a couple of days ill confirm them.

regards.

sherkat69 commented 1 year ago

hi again. the issue with my thread.sleep example is solved with calling print by a inner class.

but the UI sample is getting even weirder. only once, i could compile it but not upload. after that, what ever i try, i just get compile error.

Why I see only similar exception, and not exactly the same one ( I miss the bytecode level information)
Why you do not see th error with your second example

i don't have a clue!!!

i tried every jdk combinations. is there any way that i can see in what jdk version the jrd is run? maybe it is not passed correctly or ...

judovana commented 1 year ago

hi again. the issue with my thread.sleep example is solved with calling print by a inner class.

but the UI sample is getting even weirder. only once, i could compile it but not upload. after that, what ever i try, i just get compile error.

I'm starting to be blind. I need really precise steps how to reproduce, otherwise I will just continue to be "cooking from water" as I did until now. But the issues were at least similar to issues I already encoutnered in past, so I was trying to deduct and wrota up what I could.

Why I see only similar exception, and not exactly the same one ( I miss the bytecode level information)
Why you do not see th error with your second example

i don't have a clue!!!

I do - the https://github.com/pmikova/java-runtime-decompiler/issues/251#issuecomment-1368434672 had same trace.

i tried every jdk combinations. is there any way that i can see in what jdk version the jrd is run? maybe it is not passed correctly or ...

On linux, you can run star.sh as sh -x start.sh and the last command is resolved java path. On window, you may need to edit star.bat and do REM on.. TBH, I do not know on windows. But JAVA_HOME really should be honoured.