Skip to content

Commit

Permalink
feat: Support switch statement by enum.
Browse files Browse the repository at this point in the history
  • Loading branch information
teletha committed Mar 22, 2023
1 parent dd640c7 commit 59ef662
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 24 deletions.
8 changes: 7 additions & 1 deletion src/main/java/reincarnation/JavaClassDecompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
package reincarnation;

import static org.objectweb.asm.Opcodes.*;
import static reincarnation.OperandUtil.*;
import static reincarnation.OperandUtil.load;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
Expand All @@ -22,6 +22,7 @@
import org.objectweb.asm.Type;

import kiss.I;
import reincarnation.util.GeneratedCodes;

class JavaClassDecompiler extends ClassVisitor {

Expand Down Expand Up @@ -91,6 +92,11 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si
source.staticInitializer.add(decompiler);
} else {
Method method = source.clazz.getDeclaredMethod(name, load(parameterTypes));

if (GeneratedCodes.isEnumSwitchMethod(method)) {
return null;
}

LocalVariables locals = new LocalVariables(source.clazz, isStatic, method);
decompiler = new JavaMethodDecompiler(source, locals, returnType, method);

Expand Down
15 changes: 11 additions & 4 deletions src/main/java/reincarnation/JavaMethodDecompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
package reincarnation;

import static org.objectweb.asm.Opcodes.*;
import static reincarnation.Node.*;
import static reincarnation.Node.Termination;
import static reincarnation.OperandCondition.*;
import static reincarnation.OperandUtil.*;
import static reincarnation.OperandUtil.load;

import java.lang.reflect.Executable;
import java.lang.reflect.Method;
Expand Down Expand Up @@ -1136,8 +1136,10 @@ public void visitInsn(int opcode) {
if (enumSwitchInvoked) {
enumSwitchInvoked = false; // reset

// enum switch table starts from 1, but Enum#ordinal starts from 0
current.addOperand(current.remove(0) + "+1");
current.children(OperandMethodCall.class).to(method -> {
current.remove(0);
current.addOperand(method.owner);
});
break;
}
case AALOAD:
Expand Down Expand Up @@ -1679,6 +1681,11 @@ public void visitMethodInsn(int opcode, String className, String method, String
break;

case INVOKESTATIC: // static method call
if (GeneratedCodes.isEnumSwitchName(method)) {
enumSwitchInvoked = true;
return;
}

// Non-private static method which is called from child class have parent
// class signature.
while (!hasStaticMethod(owner, method, parameters)) {
Expand Down
33 changes: 22 additions & 11 deletions src/main/java/reincarnation/OperandMethodCall.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import reincarnation.coder.CodingOption;
import reincarnation.coder.DelegatableCoder;
import reincarnation.operator.AccessMode;
import reincarnation.util.GeneratedCodes;

class OperandMethodCall extends Operand {

Expand Down Expand Up @@ -162,20 +163,22 @@ protected boolean isNegatable() {
*/
@Override
protected void writeCode(Coder coder) {
if (!method.isSynthetic()) {
if (method.equals(StringBuilderToString)) {
Iterator<Code> concat = detectStringConcantenation(owner, new LinkedList());
// if (!method.isSynthetic()) {
if (method.equals(StringBuilderToString)) {
Iterator<Code> concat = detectStringConcantenation(owner, new LinkedList());

if (concat != null) {
coder.writeStringConcatenation(concat);
return;
}
if (concat != null) {
coder.writeStringConcatenation(concat);
return;
}
coder.writeMethodCall(method, owner, params, mode);
} else {
Code code = Reincarnation.exhume(method.getDeclaringClass()).methods.get(method);
code.write(new SyntheticMethodInliner(coder));
} else if (GeneratedCodes.isEnumSwitchMethod(method)) {
System.out.println(method + " ");
}
coder.writeMethodCall(method, owner, params, mode);
// } else {
// Code code = Reincarnation.exhume(method.getDeclaringClass()).methods.get(method);
// code.write(new SyntheticMethodInliner(coder));
// }
}

/**
Expand Down Expand Up @@ -228,6 +231,14 @@ private boolean isFactory(Method method) {
return method.getDeclaringClass() == String.class && method.getName().equals("valueOf");
}

/**
* {@inheritDoc}
*/
@Override
public Signal<Operand> children() {
return I.signal(params).startWith(owner);
}

/**
* @version 2018/10/23 14:03:05
*/
Expand Down
9 changes: 6 additions & 3 deletions src/main/java/reincarnation/Reincarnation.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import reincarnation.coder.java.JavaCoder;
import reincarnation.coder.java.JavaCodingOption;
import reincarnation.util.Classes;
import reincarnation.util.GeneratedCodes;

/**
* {@link Reincarnation} is a unit of decompilation.
Expand Down Expand Up @@ -81,7 +82,9 @@ private Reincarnation(Class clazz) {
// Separate fields into static and non-static
for (Field field : clazz.getDeclaredFields()) {
if (Classes.isStatic(field)) {
staticFields.add(field);
if (!GeneratedCodes.isEnumSwitchField(field)) {
staticFields.add(field);
}
} else {
fields.add(field);
}
Expand Down Expand Up @@ -152,10 +155,10 @@ public void require(Class[] dependencies) {
*/
public static final synchronized Reincarnation exhume(Class clazz) {
return cache.computeIfAbsent(clazz, key -> {
Reincarnation reincarnation = new Reincarnation(clazz);
Reincarnation reincarnation = new Reincarnation(key);

try {
new ClassReader(clazz.getName()).accept(new JavaClassDecompiler(reincarnation), 0);
new ClassReader(key.getName()).accept(new JavaClassDecompiler(reincarnation), 0);
} catch (IOException e) {
throw I.quiet(e);
}
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/reincarnation/coder/java/JavaCoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.function.IntFunction;

import kiss.I;
import kiss.Variable;
Expand Down Expand Up @@ -1046,12 +1047,21 @@ public void writeTryCatchFinally(Code tryBlock, List<Ⅲ<Class, String, Code>> c
* {@inheritDoc}
*/
@Override
public void writeSwitch(Optional<String> label, Code condition, Class conditionType, MultiMap<Code, Integer> caseBlocks, Code defaultBlock, Code follow) {
public void writeSwitch(Optional<String> label, Code condition, Class type, MultiMap<Code, Integer> caseBlocks, Code defaultBlock, Code follow) {
IntFunction<String> keyWriter;
if (type.isEnum()) {
keyWriter = index -> ((Enum) type.getEnumConstants()[index - 1]).name();
} else if (type == char.class) {
keyWriter = this::writeChar;
} else {
keyWriter = String::valueOf;
}

line(label(label), "switch", space, "(", condition, ")", space, "{");
indent(() -> {
caseBlocks.forEach((code, keys) -> {
for (int key : keys) {
line("case ", conditionType == char.class ? writeChar(key) : key, ":");
line("case ", keyWriter.apply(key), ":");
}
indent(() -> write(code));
});
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/reincarnation/util/GeneratedCodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,45 @@ public static boolean isEnumField(Field field) {
return false;
}

/**
* Check whether the given method is generated code or not.
*
* @param method
* @return
*/
public static boolean isEnumSwitchMethod(Method method) {
return method.isSynthetic() && isEnumSwitchName(method.getName());
}

/**
* Check whether the given field is generated code or not.
*
* @param field
* @return
*/
public static boolean isEnumSwitchField(Field field) {
return field.isSynthetic() && isEnumSwitchName(field.getName());
}

/**
* Helper method to detect special enum method.
*
* @param name
* @return
*/
public static boolean isEnumSwitchName(String name) {
// For Eclipse JDT compiler.
if (name.startsWith("$SWITCH_TABLE$")) {
return true;
}

// For JDK compiler.
if (name.startsWith("$SwitchMap$")) {
return true;
}
return false;
}

/**
* Check whether the given constructor is generated code or not.
*
Expand Down
26 changes: 23 additions & 3 deletions src/test/java/reincarnation/decompiler/flow/SwitchTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
*/
package reincarnation.decompiler.flow;

import java.lang.annotation.RetentionPolicy;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import reincarnation.CodeVerifier;
import reincarnation.Debuggable;
import reincarnation.TestCode;

class SwitchTest extends CodeVerifier {
Expand Down Expand Up @@ -1149,8 +1150,7 @@ public int run(@Param(from = 0, to = 10) int param) {
}

@Test
@Debuggable
void conditionChar() {
void conditionByChar() {
verify(new TestCode.CharParam() {

@Override
Expand All @@ -1169,6 +1169,26 @@ public char run(@Param(chars = {'a', 'b', 'c', 'd', 'e'}) char param) {
});
}

@Test
void conditionByEnum() {
verify(new TestCode.IntParam() {

@Override
public int run(@Param(from = 0, to = 3) int param) {
switch (RetentionPolicy.values()[param]) {
case CLASS:
return 10;

case RUNTIME:
return 20;

default:
return 30;
}
}
});
}

@Test
@Disabled
void switchExpression() {
Expand Down

0 comments on commit 59ef662

Please sign in to comment.