Skip to content

Commit

Permalink
fix: cross compiler more
Browse files Browse the repository at this point in the history
  • Loading branch information
teletha committed Apr 23, 2024
1 parent 44f4603 commit 76b362c
Show file tree
Hide file tree
Showing 15 changed files with 324 additions and 191 deletions.
202 changes: 154 additions & 48 deletions bytecode.txt
Original file line number Diff line number Diff line change
@@ -1,87 +1,193 @@
public class PrimitiveAndWrapperClassTest$9Dump implements Opcodes {
java.lang.ClassCastException: class reincarnation.OperandAssign cannot be cast to class reincarnation.OperandCondition (reincarnation.OperandAssign and reincarnation.OperandCondition are in unnamed module of loader 'app')
at reincarnation.Node.writeIf(Node.java:1063)
at reincarnation.Node.analyze(Node.java:965)
at reincarnation.Node.process(Node.java:1211)
at reincarnation.Node.analyze(Node.java:941)
at reincarnation.JavaMethodDecompiler.analyze(JavaMethodDecompiler.java:463)
at reincarnation.JavaMethodDecompiler.visitEnd(JavaMethodDecompiler.java:375)
at org.objectweb.asm.ClassReader.readMethod(ClassReader.java:1516)
at org.objectweb.asm.ClassReader.accept(ClassReader.java:745)
at org.objectweb.asm.ClassReader.accept(ClassReader.java:425)
at reincarnation.Reincarnation.lambda$1(Reincarnation.java:168)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
at reincarnation.Reincarnation.exhume(Reincarnation.java:161)
at reincarnation.Reincarnation.rebirth(Reincarnation.java:187)
at reincarnation.CodeVerifier.decompile(CodeVerifier.java:258)
at reincarnation.CodeVerifier.verify(CodeVerifier.java:218)
at reincarnation.decompiler.operator.InstanceOfTest.withCast(InstanceOfTest.java:194)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188)
Suppressed: reincarnation.Failuer: Decompile Error
-------------------------------------------------
Javac version
-------------------------------------------------
public class InstanceOfTest$8Dump implements Opcodes {

public static byte[] dump () throws Exception {

classWriter.visit(V21, ACC_SUPER, "reincarnation/decompiler/primitives/PrimitiveAndWrapperClassTest$9", null, "java/lang/Object", new String[] { "reincarnation/TestCode$Boolean" });
classWriter.visit(V21, ACC_SUPER, "reincarnation/decompiler/operator/InstanceOfTest$8", null, "java/lang/Object", new String[] { "reincarnation/TestCode$Text" });

{
fieldVisitor = classWriter.visitField(ACC_FINAL | ACC_STATIC | ACC_SYNTHETIC, "$assertionsDisabled", "Z", null, null);
fieldVisitor = classWriter.visitField(0, "cs", "Ljava/lang/CharSequence;", null, null);
fieldVisitor.visitEnd();
}
{
methodVisitor = classWriter.visitMethod(0, "<init>", "(Lreincarnation/decompiler/primitives/PrimitiveAndWrapperClassTest;)V", null, null);
methodVisitor = classWriter.visitMethod(0, "<init>", "(Lreincarnation/decompiler/operator/InstanceOfTest;)V", null, null);
methodVisitor.visitParameter("NoParameterName", ACC_FINAL | ACC_MANDATED);
methodVisitor.visitCode();
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(110, label0);
methodVisitor.visitLineNumber(194, label0);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
methodVisitor.visitInsn(RETURN);
Label label1 = new Label();
methodVisitor.visitLabel(label1);
methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/primitives/PrimitiveAndWrapperClassTest$9;", null, label0, label1, 0);
methodVisitor.visitLocalVariable("this$0", "Lreincarnation/decompiler/primitives/PrimitiveAndWrapperClassTest;", null, label0, label1, 1);
methodVisitor.visitMaxs(1, 2);
methodVisitor.visitLineNumber(196, label1);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitLdcInsn("test");
methodVisitor.visitFieldInsn(PUTFIELD, "reincarnation/decompiler/operator/InstanceOfTest$8", "cs", "Ljava/lang/CharSequence;");
methodVisitor.visitInsn(RETURN);
Label label2 = new Label();
methodVisitor.visitLabel(label2);
methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/operator/InstanceOfTest$8;", null, label0, label2, 0);
methodVisitor.visitLocalVariable("this$0", "Lreincarnation/decompiler/operator/InstanceOfTest;", null, label0, label2, 1);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
}
{
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "run", "()Z", null, null);
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "run", "()Ljava/lang/String;", null, null);
methodVisitor.visitCode();
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(114, label0);
methodVisitor.visitFieldInsn(GETSTATIC, "reincarnation/decompiler/primitives/PrimitiveAndWrapperClassTest$9", "$assertionsDisabled", "Z");
methodVisitor.visitLineNumber(200, label0);
methodVisitor.visitLdcInsn("");
methodVisitor.visitVarInsn(ASTORE, 1);
Label label1 = new Label();
methodVisitor.visitJumpInsn(IFNE, label1);
methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Float;"));
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;");
methodVisitor.visitJumpInsn(IF_ACMPNE, label1);
methodVisitor.visitTypeInsn(NEW, "java/lang/AssertionError");
methodVisitor.visitInsn(DUP);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/AssertionError", "<init>", "()V", false);
methodVisitor.visitInsn(ATHROW);
methodVisitor.visitLabel(label1);
methodVisitor.visitLineNumber(115, label1);
methodVisitor.visitFrame(Opcodes.F_NEW, 1, new Object[] {"reincarnation/decompiler/primitives/PrimitiveAndWrapperClassTest$9"}, 0, new Object[] {});
methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Float;"));
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;");
methodVisitor.visitLineNumber(201, label1);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, "reincarnation/decompiler/operator/InstanceOfTest$8", "cs", "Ljava/lang/CharSequence;");
methodVisitor.visitVarInsn(ASTORE, 3);
methodVisitor.visitVarInsn(ALOAD, 3);
methodVisitor.visitTypeInsn(INSTANCEOF, "java/lang/String");
Label label2 = new Label();
methodVisitor.visitJumpInsn(IF_ACMPNE, label2);
methodVisitor.visitInsn(ICONST_1);
methodVisitor.visitJumpInsn(IFEQ, label2);
methodVisitor.visitVarInsn(ALOAD, 3);
methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/String");
methodVisitor.visitVarInsn(ASTORE, 2);
Label label3 = new Label();
methodVisitor.visitJumpInsn(GOTO, label3);
methodVisitor.visitLabel(label2);
methodVisitor.visitFrame(Opcodes.F_NEW, 1, new Object[] {"reincarnation/decompiler/primitives/PrimitiveAndWrapperClassTest$9"}, 0, new Object[] {});
methodVisitor.visitInsn(ICONST_0);
methodVisitor.visitLabel(label3);
methodVisitor.visitFrame(Opcodes.F_NEW, 1, new Object[] {"reincarnation/decompiler/primitives/PrimitiveAndWrapperClassTest$9"}, 1, new Object[] {Opcodes.INTEGER});
methodVisitor.visitInsn(IRETURN);
methodVisitor.visitLineNumber(202, label3);
methodVisitor.visitVarInsn(ALOAD, 2);
methodVisitor.visitVarInsn(ASTORE, 1);
methodVisitor.visitLabel(label2);
methodVisitor.visitLineNumber(204, label2);
methodVisitor.visitFrame(Opcodes.F_NEW, 2, new Object[] {"reincarnation/decompiler/operator/InstanceOfTest$8", "java/lang/String"}, 0, new Object[] {});
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitInsn(ARETURN);
Label label4 = new Label();
methodVisitor.visitLabel(label4);
methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/primitives/PrimitiveAndWrapperClassTest$9;", null, label0, label4, 0);
methodVisitor.visitMaxs(2, 1);
methodVisitor.visitLocalVariable("text", "Ljava/lang/String;", null, label3, label2, 2);
methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/operator/InstanceOfTest$8;", null, label0, label4, 0);
methodVisitor.visitLocalVariable("result", "Ljava/lang/String;", null, label1, label4, 1);
methodVisitor.visitMaxs(1, 4);
methodVisitor.visitEnd();
}

}
}
-------------------------------------------------
ECJ version
-------------------------------------------------
public class InstanceOfTest$8Dump implements Opcodes {

public static byte[] dump () throws Exception {

classWriter.visit(V21, ACC_SUPER, "reincarnation/decompiler/operator/InstanceOfTest$8", null, "java/lang/Object", new String[] { "reincarnation/TestCode$Text" });

{
fieldVisitor = classWriter.visitField(0, "cs", "Ljava/lang/CharSequence;", null, null);
fieldVisitor.visitEnd();
}
{
methodVisitor = classWriter.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
fieldVisitor = classWriter.visitField(ACC_FINAL | ACC_SYNTHETIC, "this$0", "Lreincarnation/decompiler/operator/InstanceOfTest;", null, null);
fieldVisitor.visitEnd();
}
{
methodVisitor = classWriter.visitMethod(0, "<init>", "(Lreincarnation/decompiler/operator/InstanceOfTest;)V", null, null);
methodVisitor.visitParameter("this$0", ACC_FINAL | ACC_MANDATED);
methodVisitor.visitCode();
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(110, label0);
methodVisitor.visitLdcInsn(Type.getType("Lreincarnation/decompiler/primitives/PrimitiveAndWrapperClassTest;"));
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "desiredAssertionStatus", "()Z", false);
methodVisitor.visitLineNumber(194, label0);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitFieldInsn(PUTFIELD, "reincarnation/decompiler/operator/InstanceOfTest$8", "this$0", "Lreincarnation/decompiler/operator/InstanceOfTest;");
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
Label label1 = new Label();
methodVisitor.visitJumpInsn(IFNE, label1);
methodVisitor.visitInsn(ICONST_1);
Label label2 = new Label();
methodVisitor.visitJumpInsn(GOTO, label2);
methodVisitor.visitLabel(label1);
methodVisitor.visitFrame(Opcodes.F_NEW, 0, new Object[] {}, 0, new Object[] {});
methodVisitor.visitInsn(ICONST_0);
methodVisitor.visitLineNumber(196, label1);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitLdcInsn("test");
methodVisitor.visitFieldInsn(PUTFIELD, "reincarnation/decompiler/operator/InstanceOfTest$8", "cs", "Ljava/lang/CharSequence;");
Label label2 = new Label();
methodVisitor.visitLabel(label2);
methodVisitor.visitFrame(Opcodes.F_NEW, 0, new Object[] {}, 1, new Object[] {Opcodes.INTEGER});
methodVisitor.visitFieldInsn(PUTSTATIC, "reincarnation/decompiler/primitives/PrimitiveAndWrapperClassTest$9", "$assertionsDisabled", "Z");
methodVisitor.visitLineNumber(194, label2);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(1, 0);
methodVisitor.visitEnd();
Label label3 = new Label();
methodVisitor.visitLabel(label3);
methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/operator/InstanceOfTest$8;", null, label0, label3, 0);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
}
{
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "run", "()Ljava/lang/String;", null, null);
methodVisitor.visitCode();
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(200, label0);
methodVisitor.visitLdcInsn("");
methodVisitor.visitVarInsn(ASTORE, 1);
Label label1 = new Label();
methodVisitor.visitLabel(label1);
methodVisitor.visitLineNumber(201, label1);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, "reincarnation/decompiler/operator/InstanceOfTest$8", "cs", "Ljava/lang/CharSequence;");
methodVisitor.visitInsn(DUP);
methodVisitor.visitVarInsn(ASTORE, 3);
methodVisitor.visitTypeInsn(INSTANCEOF, "java/lang/String");
Label label2 = new Label();
methodVisitor.visitJumpInsn(IFEQ, label2);
methodVisitor.visitVarInsn(ALOAD, 3);
methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/String");
methodVisitor.visitVarInsn(ASTORE, 2);

-------------------------------------------------
Javac version
-------------------------------------------------
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "run", "()Ljava/lang/String;", null, null);
methodVisitor.visitCode();
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(200, label0);
methodVisitor.visitLdcInsn("");
methodVisitor.visitVarInsn(ASTORE, 1);
Label label1 = new Label();
methodVisitor.visitLabel(label1);
methodVisitor.visitLineNumber(201, label1);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, "reincarnation/decompiler/operator/InstanceOfTest$8", "cs", "Ljava/lang/CharSequence;");
methodVisitor.visitVarInsn(ASTORE, 3);
methodVisitor.visitVarInsn(ALOAD, 3);
methodVisitor.visitTypeInsn(INSTANCEOF, "java/lang/String");
Label label2 = new Label();
methodVisitor.visitJumpInsn(IFEQ, label2);
methodVisitor.visitVarInsn(ALOAD, 3);
methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/String");
methodVisitor.visitVarInsn(ASTORE, 2);
26 changes: 22 additions & 4 deletions src/main/java/reincarnation/JavaMethodDecompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -1888,12 +1888,28 @@ public void visitTypeInsn(int opcode, String type) {
break;

case INSTANCEOF:
// In the ECJ compiler, the instanceof operator with pattern matching generates code
// that assigns the target variable to a temporary variable. So we optimize the code to
// remove that variable and use the original variable.
if (match(DUP, ASTORE, INSTANCEOF)) {
// In ECJ compiler, the instanceof operator with pattern matching generates code
// that assigns the target variable to a temporary variable. So we optimize the code
// to remove that variable and use the original variable.
//
// ================= Sample Bytecode =================
// methodVisitor.visitInsn(DUP);
// methodVisitor.visitVarInsn(ASTORE, 3);
// methodVisitor.visitTypeInsn(INSTANCEOF, "java/lang/String");
Operand extra = current.remove(1);
current.remove(0).as(OperandAssign.class).exact().assignedTo(extra).to(current::addOperand);
} else if (match(ASTORE, ALOAD, INSTANCEOF)) {
// In Javac compiler, the instanceof operator with pattern matching generates code
// that assigns the target variable to a temporary variable. So we optimize the code
// to remove that variable and use the original variable.
//
// ================= Sample Bytecode =================
// methodVisitor.visitVarInsn(ASTORE, 3);
// methodVisitor.visitVarInsn(ALOAD, 3);
// methodVisitor.visitTypeInsn(INSTANCEOF, "java/lang/String");
Operand extra = current.remove(0);
current.remove(0).as(OperandAssign.class).exact().assignedTo(extra).to(current::addOperand);
}

source.require(clazz);
Expand Down Expand Up @@ -1949,7 +1965,9 @@ public void visitVarInsn(int opcode, int position) {

case ASTORE:
// instanceof with cast produces special bytecode, so we must handle it by special way.
if (match(DUP, ASTORE, INSTANCEOF, IFEQ, ALOAD, CHECKCAST, ASTORE)) {
// For ECJ - DUP STORE INSTANCEOF ....
// For Javac - ASTORE ALOAD INSTANCEOF ...
if (match(DUP, ASTORE, INSTANCEOF, IFEQ, ALOAD, CHECKCAST, ASTORE) || match(ASTORE, ALOAD, INSTANCEOF, IFEQ, ALOAD, CHECKCAST, ASTORE)) {
current.remove(0);
current.peek(0).children(OperandInstanceOf.class).to(o -> o.withCast(variable));
return;
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/reincarnation/OperandAssign.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ final Signal<Operand> assignedTo(Operand operand) {
return isAssignedTo(operand) ? I.signal(right) : I.signal();
}

/**
* Select the assign value on this operand.
*
* @return
*/
final Signal<Operand> assign() {
return I.signal(right);
}

/**
* Change to the shorter assignment if possible.
*/
Expand Down
27 changes: 21 additions & 6 deletions src/test/java/reincarnation/CodeVerifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,7 @@ protected final void verify(TestCode code) {
.reason(format(decompiled, true));

if (CompilerType.isJavac()) {
failuer //
.reason("-------------------------------------------------")
failuer.reason("-------------------------------------------------")
.reason(" Javac version")
.reason("-------------------------------------------------")
.reason(asmifier(target))
Expand All @@ -213,11 +212,27 @@ protected final void verify(TestCode code) {
}
} catch (Throwable e) {
if (debugLog.isEmpty() && debugged.add(target)) {
// decompile with debug mode
Reincarnation.cache.remove(target);
decompiled = decompile(target, true);
try {
// decompile with debug mode
Reincarnation.cache.remove(target);
decompiled = decompile(target, true);
} catch (Throwable decompileError) {
if (CompilerType.isJavac()) {
Failuer failuer = Failuer.type("Decompile Error")
.reason("-------------------------------------------------")
.reason(" Javac version")
.reason("-------------------------------------------------")
.reason(asmifier(target))
.reason("-------------------------------------------------")
.reason(" ECJ version")
.reason("-------------------------------------------------")
.reason(asmifier(code.getClass()));
decompileError.addSuppressed(failuer);
}
throw decompileError;
}
}
throw I.quiet(e);
throw e;
} finally {
if (!debugLog.isEmpty()) {
for (String line : format(decompiled, true)) {
Expand Down
16 changes: 16 additions & 0 deletions src/test/java/reincarnation/Failuer.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ public static Failuer type(String type) {
return failuer;
}

/**
* Create wrapped error.
*
* @param error
* @return
*/
public static Failuer wrap(Throwable error) {
if (error instanceof Failuer failuer) {
return failuer;
}

Failuer failuer = type(error.getMessage());
failuer.addSuppressed(error);
return failuer;
}

/**
* Add cause of this {@link Failuer}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,13 @@
*/
package reincarnation.decompiler.operator;

import org.junit.jupiter.api.Test;

import reincarnation.TestCode;
import reincarnation.CodeVerifier;
import reincarnation.DecompilableTest;
import reincarnation.TestCode;

/**
* @version 2018/10/22 17:01:57
*/
class ArithmeticOperatorPriorityTest extends CodeVerifier {

@Test
@DecompilableTest
void shiftWithAdd() {
verify(new TestCode.Run() {

Expand Down
Loading

0 comments on commit 76b362c

Please sign in to comment.