Commit b9fffa14 authored by Skylot's avatar Skylot

fix: allow override type with wider one only from debug info (#403)

parent 37071dba
...@@ -526,6 +526,11 @@ public abstract class ArgType { ...@@ -526,6 +526,11 @@ public abstract class ArgType {
return isArray() || (!isTypeKnown() && contains(PrimitiveType.ARRAY)); return isArray() || (!isTypeKnown() && contains(PrimitiveType.ARRAY));
} }
public boolean canBePrimitive(PrimitiveType primitiveType) {
return (isPrimitive() && getPrimitiveType() == primitiveType)
|| (!isTypeKnown() && contains(primitiveType));
}
public static ArgType convertFromPrimitiveType(PrimitiveType primitiveType) { public static ArgType convertFromPrimitiveType(PrimitiveType primitiveType) {
switch (primitiveType) { switch (primitiveType) {
case BOOLEAN: case BOOLEAN:
......
...@@ -9,6 +9,7 @@ import java.util.Set; ...@@ -9,6 +9,7 @@ import java.util.Set;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jadx.core.Consts;
import jadx.core.deobf.NameMapper; import jadx.core.deobf.NameMapper;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
...@@ -34,8 +35,8 @@ import jadx.core.utils.ErrorsCounter; ...@@ -34,8 +35,8 @@ import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxException;
@JadxVisitor( @JadxVisitor(
name = "Debug Info Parser", name = "Debug Info Apply",
desc = "Parse debug information (variable names and types, instruction lines)", desc = "Apply debug info to registers (type and names)",
runAfter = { runAfter = {
SSATransform.class, SSATransform.class,
TypeInferenceVisitor.class, TypeInferenceVisitor.class,
...@@ -101,7 +102,7 @@ public class DebugInfoApplyVisitor extends AbstractVisitor { ...@@ -101,7 +102,7 @@ public class DebugInfoApplyVisitor extends AbstractVisitor {
int startAddr = localVar.getStartAddr(); int startAddr = localVar.getStartAddr();
int endAddr = localVar.getEndAddr(); int endAddr = localVar.getEndAddr();
if (isInside(startOffset, startAddr, endAddr) || isInside(endOffset, startAddr, endAddr)) { if (isInside(startOffset, startAddr, endAddr) || isInside(endOffset, startAddr, endAddr)) {
if (LOG.isDebugEnabled()) { if (Consts.DEBUG && LOG.isDebugEnabled()) {
LOG.debug("Apply debug info by offset for: {} to {}", ssaVar, localVar); LOG.debug("Apply debug info by offset for: {} to {}", ssaVar, localVar);
} }
applyDebugInfo(mth, ssaVar, localVar.getType(), localVar.getName()); applyDebugInfo(mth, ssaVar, localVar.getType(), localVar.getName());
...@@ -126,15 +127,15 @@ public class DebugInfoApplyVisitor extends AbstractVisitor { ...@@ -126,15 +127,15 @@ public class DebugInfoApplyVisitor extends AbstractVisitor {
} }
public static void applyDebugInfo(MethodNode mth, SSAVar ssaVar, ArgType type, String varName) { public static void applyDebugInfo(MethodNode mth, SSAVar ssaVar, ArgType type, String varName) {
TypeUpdateResult result = mth.root().getTypeUpdate().apply(ssaVar, type); if (NameMapper.isValidIdentifier(varName)) {
ssaVar.setName(varName);
}
TypeUpdateResult result = mth.root().getTypeUpdate().applyDebug(ssaVar, type);
if (result == TypeUpdateResult.REJECT) { if (result == TypeUpdateResult.REJECT) {
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("Reject debug info of type: {} and name: '{}' for {}, mth: {}", type, varName, ssaVar, mth); LOG.debug("Reject debug info of type: {} and name: '{}' for {}, mth: {}", type, varName, ssaVar, mth);
} }
} else { } else {
if (NameMapper.isValidIdentifier(varName)) {
ssaVar.setName(varName);
}
detachDebugInfo(ssaVar.getAssign()); detachDebugInfo(ssaVar.getAssign());
ssaVar.getUseList().forEach(DebugInfoApplyVisitor::detachDebugInfo); ssaVar.getUseList().forEach(DebugInfoApplyVisitor::detachDebugInfo);
} }
......
...@@ -193,9 +193,6 @@ public class TypeCompare { ...@@ -193,9 +193,6 @@ public class TypeCompare {
return comparator; return comparator;
} }
/**
*
*/
private final class ArgTypeComparator implements Comparator<ArgType> { private final class ArgTypeComparator implements Comparator<ArgType> {
@Override @Override
public int compare(ArgType a, ArgType b) { public int compare(ArgType a, ArgType b) {
......
...@@ -33,11 +33,22 @@ public final class TypeUpdate { ...@@ -33,11 +33,22 @@ public final class TypeUpdate {
private final TypeUpdateRegistry listenerRegistry; private final TypeUpdateRegistry listenerRegistry;
private final TypeCompare comparator; private final TypeCompare comparator;
private ThreadLocal<Boolean> applyDebug = new ThreadLocal<>();
public TypeUpdate(RootNode root) { public TypeUpdate(RootNode root) {
this.listenerRegistry = initListenerRegistry(); this.listenerRegistry = initListenerRegistry();
this.comparator = new TypeCompare(root); this.comparator = new TypeCompare(root);
} }
public TypeUpdateResult applyDebug(SSAVar ssaVar, ArgType candidateType) {
try {
applyDebug.set(true);
return apply(ssaVar, candidateType);
} finally {
applyDebug.set(false);
}
}
public TypeUpdateResult apply(SSAVar ssaVar, ArgType candidateType) { public TypeUpdateResult apply(SSAVar ssaVar, ArgType candidateType) {
if (candidateType == null) { if (candidateType == null) {
return REJECT; return REJECT;
...@@ -71,6 +82,16 @@ public final class TypeUpdate { ...@@ -71,6 +82,16 @@ public final class TypeUpdate {
if (arg.isTypeImmutable() && currentType != ArgType.UNKNOWN) { if (arg.isTypeImmutable() && currentType != ArgType.UNKNOWN) {
return REJECT; return REJECT;
} }
TypeCompareEnum compareResult = comparator.compareTypes(candidateType, currentType);
if (compareResult == TypeCompareEnum.CONFLICT) {
return REJECT;
}
if (compareResult == TypeCompareEnum.WIDER || compareResult == TypeCompareEnum.WIDER_BY_GENERIC) {
// allow wider types for apply from debug info
if (applyDebug.get() != Boolean.TRUE) {
return REJECT;
}
}
if (arg instanceof RegisterArg) { if (arg instanceof RegisterArg) {
RegisterArg reg = (RegisterArg) arg; RegisterArg reg = (RegisterArg) arg;
return updateTypeForSsaVar(updateInfo, reg.getSVar(), candidateType); return updateTypeForSsaVar(updateInfo, reg.getSVar(), candidateType);
...@@ -316,6 +337,14 @@ public final class TypeUpdate { ...@@ -316,6 +337,14 @@ public final class TypeUpdate {
if (candidateType.isArray() && updateArgType.canBeArray()) { if (candidateType.isArray() && updateArgType.canBeArray()) {
return SAME; return SAME;
} }
if (candidateType.isPrimitive()) {
if (updateArgType.canBePrimitive(candidateType.getPrimitiveType())) {
return SAME;
}
if (updateArgType.isTypeKnown() && candidateType.getRegCount() == updateArgType.getRegCount()) {
return SAME;
}
}
} }
return result; return result;
} }
......
...@@ -19,6 +19,9 @@ public class TestInlineInCatch extends IntegrationTest { ...@@ -19,6 +19,9 @@ public class TestInlineInCatch extends IntegrationTest {
File output = null; File output = null;
try { try {
output = File.createTempFile("f", "a", dir); output = File.createTempFile("f", "a", dir);
if (!output.exists()) {
return 1;
}
return 0; return 0;
} catch (Exception e) { } catch (Exception e) {
if (output != null) { if (output != null) {
......
package jadx.tests.integration.types;
import org.junit.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class TestPrimitivesInIf extends IntegrationTest {
public static class TestCls {
public boolean test(String str) {
short sh = Short.parseShort(str);
int i = Integer.parseInt(str);
System.out.println(sh + " vs " + i);
return sh == i;
}
public void check() {
assertTrue(test("1"));
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("short sh = Short.parseShort(str);"));
assertThat(code, containsOne("int i = Integer.parseInt(str);"));
assertThat(code, containsOne("return sh == i;"));
}
@Test
public void test2() {
setOutputCFG();
noDebugInfo();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("short parseShort = Short.parseShort(str);"));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment