提交 c0194d02 编写于 作者: S Skylot

refactor: fix misuse of immutable type flag

上级 19ca8a09
......@@ -4,15 +4,12 @@ import java.util.Objects;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
public class RegisterArg extends InsnArg implements Named {
private static final Logger LOG = LoggerFactory.getLogger(RegisterArg.class);
public static final String THIS_ARG_NAME = "this";
protected final int regNum;
......@@ -38,15 +35,6 @@ public class RegisterArg extends InsnArg implements Named {
if (sVar == null) {
throw new JadxRuntimeException("Can't change type for register without SSA variable: " + this);
}
if (contains(AFlag.IMMUTABLE_TYPE)) {
if (!type.isTypeKnown()) {
throw new JadxRuntimeException("Unknown immutable type '" + type + "' in " + this);
}
if (!type.equals(newType)) {
LOG.warn("JADX WARNING: Can't change immutable type from '{}' to '{}' for {}", type, newType, this);
return;
}
}
sVar.setType(newType);
}
......@@ -62,9 +50,23 @@ public class RegisterArg extends InsnArg implements Named {
return type;
}
@Nullable
public ArgType getImmutableType() {
if (contains(AFlag.IMMUTABLE_TYPE)) {
return type;
}
if (sVar != null) {
return sVar.getImmutableType();
}
return null;
}
@Override
public boolean isTypeImmutable() {
return contains(AFlag.IMMUTABLE_TYPE) || (sVar != null && sVar.contains(AFlag.IMMUTABLE_TYPE));
if (contains(AFlag.IMMUTABLE_TYPE)) {
return true;
}
return sVar != null && sVar.isTypeImmutable();
}
public SSAVar getSVar() {
......@@ -73,9 +75,14 @@ public class RegisterArg extends InsnArg implements Named {
void setSVar(@NotNull SSAVar sVar) {
this.sVar = sVar;
if (contains(AFlag.IMMUTABLE_TYPE)) {
sVar.add(AFlag.IMMUTABLE_TYPE);
}
@Override
public void add(AFlag flag) {
if (flag == AFlag.IMMUTABLE_TYPE && !type.isTypeKnown()) {
throw new JadxRuntimeException("Can't mark unknown type as immutable, type: " + type + ", reg: " + this);
}
super.add(flag);
}
@Override
......@@ -169,8 +176,7 @@ public class RegisterArg extends InsnArg implements Named {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("(r");
sb.append(regNum);
sb.append("(r").append(regNum);
if (sVar != null) {
sb.append('v').append(sVar.getVersion());
}
......
......@@ -9,8 +9,8 @@ import java.util.Set;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrNode;
import jadx.core.dex.attributes.nodes.RegDebugInfoAttr;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.PhiInsn;
......@@ -20,7 +20,7 @@ import jadx.core.dex.visitors.typeinference.TypeInfo;
import jadx.core.utils.StringUtils;
import jadx.core.utils.exceptions.JadxRuntimeException;
public class SSAVar extends AttrNode {
public class SSAVar {
private final int regNum;
private final int version;
......@@ -66,8 +66,29 @@ public class SSAVar extends AttrNode {
return useList.size();
}
// must be used only from RegisterArg#setType()
void setType(ArgType type) {
@Nullable
public ArgType getImmutableType() {
if (assign.contains(AFlag.IMMUTABLE_TYPE)) {
return assign.getInitType();
}
for (RegisterArg useArg : useList) {
if (useArg.contains(AFlag.IMMUTABLE_TYPE)) {
return useArg.getInitType();
}
}
return null;
}
public boolean isTypeImmutable() {
return getImmutableType() != null;
}
public void setType(ArgType type) {
ArgType imType = getImmutableType();
if (imType != null && !imType.equals(type)) {
throw new JadxRuntimeException("Can't change immutable type " + imType + " to " + type + " for " + this);
}
typeInfo.setType(type);
if (codeVar != null) {
codeVar.setType(type);
......
......@@ -244,12 +244,12 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode {
return;
}
argsList = new ArrayList<>(args.size());
for (ArgType arg : args) {
RegisterArg regArg = InsnArg.reg(pos, arg);
for (ArgType argType : args) {
RegisterArg regArg = InsnArg.reg(pos, argType);
regArg.add(AFlag.METHOD_ARGUMENT);
regArg.add(AFlag.IMMUTABLE_TYPE);
argsList.add(regArg);
pos += arg.getRegCount();
pos += argType.getRegCount();
}
}
......
......@@ -134,8 +134,7 @@ public class DeboxingVisitor extends AbstractVisitor {
private boolean canChangeTypeToPrimitive(RegisterArg arg) {
for (SSAVar ssaVar : arg.getSVar().getCodeVar().getSsaVars()) {
RegisterArg assignArg = ssaVar.getAssign();
if (assignArg.contains(AFlag.IMMUTABLE_TYPE)) {
if (ssaVar.isTypeImmutable()) {
return false;
}
for (RegisterArg useArg : ssaVar.getUseList()) {
......
......@@ -2,6 +2,7 @@ package jadx.core.dex.visitors;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
......@@ -89,8 +90,8 @@ public class InitCodeVariables extends AbstractVisitor {
private static void setCodeVarType(CodeVar codeVar, Set<SSAVar> vars) {
if (vars.size() > 1) {
List<ArgType> imTypes = vars.stream()
.filter(var -> var.contains(AFlag.IMMUTABLE_TYPE))
.map(var -> var.getTypeInfo().getType())
.map(SSAVar::getImmutableType)
.filter(Objects::nonNull)
.filter(ArgType::isTypeKnown)
.distinct()
.collect(Collectors.toList());
......
......@@ -294,7 +294,7 @@ public class ModVisitor extends AbstractVisitor {
SSAVar sVar = reg.getSVar();
if (sVar != null) {
sVar.getCodeVar().setFinal(true);
sVar.add(AFlag.DONT_INLINE);
sVar.getAssign().add(AFlag.DONT_INLINE);
}
reg.add(AFlag.SKIP_ARG);
}
......
......@@ -337,9 +337,16 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
if (!iterableArg.isRegister() || !iterableType.isObject()) {
return true;
}
// TODO: add checks
iterableType = ArgType.generic(iterableType.getObject(), varType);
iterableArg.setType(iterableType);
ArgType genericType = ArgType.generic(iterableType.getObject(), varType);
if (iterableArg.isRegister()) {
ArgType immutableType = ((RegisterArg) iterableArg).getImmutableType();
if (immutableType != null && !immutableType.equals(genericType)) {
// can't change type
// allow to iterate over not generified collection only for Object vars
return varType.equals(ArgType.OBJECT);
}
}
iterableArg.setType(genericType);
return true;
}
......
......@@ -793,8 +793,7 @@ public class RegionMaker {
LOG.debug("Fixing incorrect switch cases order, method: {}", mth);
blocksMap = reOrderSwitchCases(blocksMap, fallThroughCases);
if (isBadCasesOrder(blocksMap, fallThroughCases)) {
LOG.error("Can't fix incorrect switch cases order, method: {}", mth);
mth.add(AFlag.INCONSISTENT_CODE);
mth.addWarn("Can't fix incorrect switch cases order");
}
}
......
......@@ -72,11 +72,10 @@ public class ProcessVariables extends AbstractVisitor {
codeVar.setType(ArgType.UNKNOWN);
unknownTypesCount++;
} else {
codeVar.getSsaVars().stream()
.filter(ssaVar -> ssaVar.contains(AFlag.IMMUTABLE_TYPE))
codeVar.getSsaVars()
.forEach(ssaVar -> {
ArgType ssaType = ssaVar.getAssign().getInitType();
if (ssaType.isTypeKnown()) {
ArgType ssaType = ssaVar.getImmutableType();
if (ssaType != null && ssaType.isTypeKnown()) {
TypeCompare comparator = mth.root().getTypeUpdate().getTypeCompare();
TypeCompareEnum result = comparator.compareTypes(ssaType, codeVarType);
if (result == TypeCompareEnum.CONFLICT || result.isNarrow()) {
......
......@@ -75,7 +75,7 @@ public class CodeShrinkVisitor extends AbstractVisitor {
private static void checkInline(MethodNode mth, BlockNode block, InsnList insnList,
List<WrapInfo> wrapList, ArgsInfo argsInfo, RegisterArg arg) {
SSAVar sVar = arg.getSVar();
if (sVar == null || sVar.contains(AFlag.DONT_INLINE)) {
if (sVar == null || sVar.getAssign().contains(AFlag.DONT_INLINE)) {
return;
}
// allow inline only one use arg
......
......@@ -2,7 +2,6 @@ package jadx.core.dex.visitors.ssa;
import java.util.Arrays;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.BlockNode;
......@@ -23,13 +22,10 @@ final class RenameState {
new int[regsCount]);
RegisterArg thisArg = mth.getThisArg();
if (thisArg != null) {
SSAVar ssaVar = state.startVar(thisArg);
ssaVar.add(AFlag.THIS);
ssaVar.add(AFlag.METHOD_ARGUMENT);
state.startVar(thisArg);
}
for (RegisterArg arg : mth.getArgRegs()) {
SSAVar ssaVar = state.startVar(arg);
ssaVar.add(AFlag.METHOD_ARGUMENT);
state.startVar(arg);
}
return state;
}
......
......@@ -107,7 +107,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
for (SSAVar var : mth.getSVars()) {
ArgType type = var.getTypeInfo().getType();
if (!type.isTypeKnown()
&& !var.getAssign().isTypeImmutable()
&& !var.isTypeImmutable()
&& !tryDeduceType(mth, var, type)) {
resolved = false;
}
......@@ -131,20 +131,9 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
private boolean setImmutableType(SSAVar ssaVar) {
try {
ArgType codeVarType = ssaVar.getCodeVar().getType();
if (codeVarType != null) {
return applyImmutableType(ssaVar, codeVarType);
}
RegisterArg assignArg = ssaVar.getAssign();
if (assignArg.isTypeImmutable()) {
return applyImmutableType(ssaVar, assignArg.getInitType());
}
if (ssaVar.contains(AFlag.IMMUTABLE_TYPE)) {
for (RegisterArg arg : ssaVar.getUseList()) {
if (arg.isTypeImmutable()) {
return applyImmutableType(ssaVar, arg.getInitType());
}
}
ArgType immutableType = ssaVar.getImmutableType();
if (immutableType != null) {
return applyImmutableType(ssaVar, immutableType);
}
return false;
} catch (Exception e) {
......@@ -166,7 +155,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
TypeUpdateResult result = typeUpdate.apply(ssaVar, initType);
if (result == TypeUpdateResult.REJECT) {
if (Consts.DEBUG) {
LOG.warn("Initial immutable type set rejected: {} -> {}", ssaVar, initType);
LOG.info("Reject initial immutable type {} for {}", initType, ssaVar);
}
return false;
}
......@@ -236,8 +225,13 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
}
private void addAssignBound(TypeInfo typeInfo, RegisterArg assign) {
ArgType immutableType = assign.getImmutableType();
if (immutableType != null) {
addBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, immutableType));
return;
}
InsnNode insn = assign.getParentInsn();
if (insn == null || assign.isTypeImmutable()) {
if (insn == null || insn.getResult() == null) {
addBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, assign.getInitType()));
return;
}
......@@ -436,12 +430,12 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
}
private void processIncompatiblePrimitives(MethodNode mth, SSAVar var) {
if (var.getAssign().getType() == ArgType.BOOLEAN) {
if (var.getTypeInfo().getType() == ArgType.BOOLEAN) {
for (ITypeBound bound : var.getTypeInfo().getBounds()) {
if (bound.getBound() == BoundEnum.USE
&& bound.getType().isPrimitive() && bound.getType() != ArgType.BOOLEAN) {
InsnNode insn = bound.getArg().getParentInsn();
if (insn.getType() == InsnType.CAST) {
if (insn == null || insn.getType() == InsnType.CAST) {
continue;
}
......
......@@ -77,10 +77,7 @@ public class TypeSearch {
for (TypeSearchVarInfo var : resolvedVars) {
SSAVar ssaVar = var.getVar();
ArgType resolvedType = var.getCurrentType();
ssaVar.getAssign().setType(resolvedType);
for (RegisterArg arg : ssaVar.getUseList()) {
arg.setType(resolvedType);
}
ssaVar.setType(resolvedType);
}
boolean applySuccess = true;
for (TypeSearchVarInfo var : resolvedVars) {
......@@ -194,18 +191,14 @@ public class TypeSearch {
private void fillTypeCandidates(SSAVar ssaVar) {
TypeSearchVarInfo varInfo = state.getVarInfo(ssaVar);
ArgType currentType = ssaVar.getTypeInfo().getType();
if (currentType.isTypeKnown()) {
varInfo.setTypeResolved(true);
varInfo.setCurrentType(currentType);
varInfo.setCandidateTypes(Collections.emptyList());
ArgType immutableType = ssaVar.getImmutableType();
if (immutableType != null) {
varInfo.markResolved(immutableType);
return;
}
if (ssaVar.getAssign().isTypeImmutable()) {
ArgType initType = ssaVar.getAssign().getInitType();
varInfo.setTypeResolved(true);
varInfo.setCurrentType(initType);
varInfo.setCandidateTypes(Collections.emptyList());
ArgType currentType = ssaVar.getTypeInfo().getType();
if (currentType.isTypeKnown()) {
varInfo.markResolved(currentType);
return;
}
......
package jadx.core.dex.visitors.typeinference;
import java.util.Collections;
import java.util.List;
import jadx.core.dex.instructions.args.ArgType;
......@@ -17,6 +18,12 @@ public class TypeSearchVarInfo {
this.var = var;
}
public void markResolved(ArgType type) {
this.currentType = type;
this.typeResolved = true;
this.candidateTypes = Collections.emptyList();
}
public void reset() {
if (typeResolved) {
return;
......
......@@ -122,6 +122,13 @@ public final class TypeUpdate {
private TypeUpdateResult updateTypeForSsaVar(TypeUpdateInfo updateInfo, SSAVar ssaVar, ArgType candidateType) {
TypeInfo typeInfo = ssaVar.getTypeInfo();
ArgType immutableType = ssaVar.getImmutableType();
if (immutableType != null && !Objects.equals(immutableType, candidateType)) {
if (Consts.DEBUG) {
LOG.info("Reject change immutable type {} to {} for {}", immutableType, candidateType, ssaVar);
}
return REJECT;
}
if (!inBounds(updateInfo, typeInfo.getBounds(), candidateType)) {
if (Consts.DEBUG) {
LOG.debug("Reject type '{}' for {} by bounds: {}", candidateType, ssaVar, typeInfo.getBounds());
......@@ -363,15 +370,20 @@ public final class TypeUpdate {
boolean assignChanged = isAssign(insn, arg);
InsnArg changeArg = assignChanged ? insn.getArg(0) : insn.getResult();
// allow result to be wider
TypeCompareEnum cmp = comparator.compareTypes(candidateType, changeArg.getType());
boolean correctType = cmp.isEqual() || (assignChanged ? cmp.isWider() : cmp.isNarrow());
boolean correctType;
if (changeArg.getType().isTypeKnown()) {
// allow result to be wider
TypeCompareEnum cmp = comparator.compareTypes(candidateType, changeArg.getType());
correctType = cmp.isEqual() || (assignChanged ? cmp.isWider() : cmp.isNarrow());
} else {
correctType = true;
}
TypeUpdateResult result = updateTypeChecked(updateInfo, changeArg, candidateType);
if (result == SAME && !correctType) {
if (Consts.DEBUG) {
LOG.debug("Move insn types mismatch: {} -> {}, insn: {}",
candidateType, changeArg.getType(), insn);
LOG.debug("Move insn types mismatch: {} -> {}, change arg: {}, insn: {}",
candidateType, changeArg.getType(), changeArg, insn);
}
return REJECT;
}
......
package jadx.tests.integration.inline;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.hamcrest.MatcherAssert.assertThat;
public class TestTernaryCast extends IntegrationTest {
public static class TestCls {
public String test(boolean b, Object obj, CharSequence cs) {
return (String) (b ? obj : cs);
}
public void check() {
assertThat(test(true, "a", "b"), Matchers.is("a"));
assertThat(test(false, "a", "b"), Matchers.is("b"));
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("return (String) (b ? obj : cs);"));
}
@Test
public void testNoDebug() {
noDebugInfo();
getClassNode(TestCls.class);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册