fix: inline anonymous classes with not default constructor (#450)
This commit is contained in:
@@ -582,14 +582,14 @@ public class InsnGen {
|
||||
} else {
|
||||
parent = cls.getSuperClass();
|
||||
}
|
||||
MethodNode defCtr = cls.getDefaultConstructor();
|
||||
if (defCtr != null) {
|
||||
if (RegionUtils.notEmpty(defCtr.getRegion())) {
|
||||
defCtr.add(AFlag.ANONYMOUS_CONSTRUCTOR);
|
||||
} else {
|
||||
defCtr.add(AFlag.DONT_GENERATE);
|
||||
// hide empty anonymous constructors
|
||||
for (MethodNode ctor : cls.getMethods()) {
|
||||
if (ctor.contains(AFlag.ANONYMOUS_CONSTRUCTOR)
|
||||
&& RegionUtils.isEmpty(ctor.getRegion())) {
|
||||
ctor.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
}
|
||||
|
||||
code.add("new ");
|
||||
if (parent == null) {
|
||||
code.add("Object");
|
||||
|
||||
@@ -34,6 +34,7 @@ import jadx.core.dex.nodes.parser.AnnotationsParser;
|
||||
import jadx.core.dex.nodes.parser.FieldInitAttr;
|
||||
import jadx.core.dex.nodes.parser.SignatureParser;
|
||||
import jadx.core.dex.nodes.parser.StaticValuesParser;
|
||||
import jadx.core.utils.RegionUtils;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
@@ -124,7 +125,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode {
|
||||
accFlagsValue = cls.getAccessFlags();
|
||||
}
|
||||
this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS);
|
||||
markAnonymousClass(this);
|
||||
markAnonymousClass();
|
||||
buildCache();
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Error decode class: " + clsInfo, e);
|
||||
@@ -403,10 +404,25 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode {
|
||||
&& getSuperClass().getObject().equals(ArgType.ENUM.getObject());
|
||||
}
|
||||
|
||||
public boolean markAnonymousClass() {
|
||||
if (isAnonymous() || isLambdaCls()) {
|
||||
add(AFlag.ANONYMOUS_CLASS);
|
||||
add(AFlag.DONT_GENERATE);
|
||||
|
||||
for (MethodNode mth : getMethods()) {
|
||||
if (mth.isConstructor()) {
|
||||
mth.add(AFlag.ANONYMOUS_CONSTRUCTOR);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isAnonymous() {
|
||||
return clsInfo.isInner()
|
||||
&& clsInfo.getAlias().getShortName().startsWith(Consts.ANONYMOUS_CLASS_PREFIX)
|
||||
&& getDefaultConstructor() != null;
|
||||
&& Character.isDigit(clsInfo.getShortName().charAt(0))
|
||||
&& methods.stream().filter(MethodNode::isConstructor).count() == 1;
|
||||
}
|
||||
|
||||
public boolean isLambdaCls() {
|
||||
@@ -425,13 +441,6 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode {
|
||||
return c;
|
||||
}
|
||||
|
||||
private static void markAnonymousClass(ClassNode cls) {
|
||||
if (cls.isAnonymous() || cls.isLambdaCls()) {
|
||||
cls.add(AFlag.ANONYMOUS_CLASS);
|
||||
cls.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MethodNode getClassInitMth() {
|
||||
return searchMethodByShortId("<clinit>()V");
|
||||
|
||||
@@ -552,9 +552,12 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isConstructor() {
|
||||
return accFlags.isConstructor() && mthInfo.isConstructor();
|
||||
}
|
||||
|
||||
public boolean isDefaultConstructor() {
|
||||
boolean result = false;
|
||||
if (accFlags.isConstructor() && mthInfo.isConstructor()) {
|
||||
if (isConstructor()) {
|
||||
int defaultArgCount = 0;
|
||||
// workaround for non-static inner class constructor, that has synthetic argument
|
||||
if (parentClass.getClassInfo().isInner()
|
||||
@@ -565,9 +568,9 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode {
|
||||
defaultArgCount = 1;
|
||||
}
|
||||
}
|
||||
result = argsList == null || argsList.size() == defaultArgCount;
|
||||
return argsList == null || argsList.size() == defaultArgCount;
|
||||
}
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isVirtual() {
|
||||
|
||||
@@ -51,10 +51,11 @@ public class ClassModifier extends AbstractVisitor {
|
||||
cls.add(AFlag.DONT_GENERATE);
|
||||
return false;
|
||||
}
|
||||
markAnonymousClass(cls);
|
||||
cls.markAnonymousClass();
|
||||
removeSyntheticFields(cls);
|
||||
cls.getMethods().forEach(ClassModifier::removeSyntheticMethods);
|
||||
cls.getMethods().forEach(ClassModifier::removeEmptyMethods);
|
||||
cls.getMethods().forEach(ClassModifier::cleanInsnsInAnonymousConstructor);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -65,13 +66,6 @@ public class ClassModifier extends AbstractVisitor {
|
||||
&& cls.getInnerClasses().isEmpty();
|
||||
}
|
||||
|
||||
private void markAnonymousClass(ClassNode cls) {
|
||||
if (cls.isAnonymous()) {
|
||||
cls.add(AFlag.ANONYMOUS_CLASS);
|
||||
cls.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove synthetic fields if type is outer class or class will be inlined (anonymous)
|
||||
*/
|
||||
@@ -324,12 +318,37 @@ public class ClassModifier extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove super call and put into removed fields from anonymous constructor
|
||||
*/
|
||||
private static void cleanInsnsInAnonymousConstructor(MethodNode mth) {
|
||||
if (!mth.contains(AFlag.ANONYMOUS_CONSTRUCTOR)) {
|
||||
return;
|
||||
}
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
InsnType type = insn.getType();
|
||||
if (type == InsnType.CONSTRUCTOR) {
|
||||
ConstructorInsn ctorInsn = (ConstructorInsn) insn;
|
||||
if (ctorInsn.isSuper()) {
|
||||
ctorInsn.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
} else if (type == InsnType.IPUT) {
|
||||
FieldInfo fldInfo = (FieldInfo) ((IndexInsnNode) insn).getIndex();
|
||||
FieldNode fieldNode = mth.dex().resolveField(fldInfo);
|
||||
if (fieldNode != null && fieldNode.contains(AFlag.DONT_GENERATE)) {
|
||||
insn.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isNonDefaultConstructorExists(MethodNode defCtor) {
|
||||
ClassNode parentClass = defCtor.getParentClass();
|
||||
for (MethodNode mth : parentClass.getMethods()) {
|
||||
if (mth != defCtor
|
||||
&& mth.getAccessFlags().isConstructor()
|
||||
&& mth.getMethodInfo().isConstructor()
|
||||
&& mth.isConstructor()
|
||||
&& !mth.isDefaultConstructor()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.ArithNode;
|
||||
@@ -39,8 +38,8 @@ import jadx.core.dex.trycatch.ExcHandlerAttr;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.dex.visitors.shrink.CodeShrinkVisitor;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.InsnRemover;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import static jadx.core.utils.BlockUtils.replaceInsn;
|
||||
@@ -207,23 +206,22 @@ public class ModVisitor extends AbstractVisitor {
|
||||
if (callMthNode == null) {
|
||||
return;
|
||||
}
|
||||
ClassNode classNode = callMthNode.getParentClass();
|
||||
ClassInfo classInfo = classNode.getClassInfo();
|
||||
ClassNode parentClass = mth.getParentClass();
|
||||
if (!classInfo.isInner()
|
||||
|| !Character.isDigit(classInfo.getShortName().charAt(0))
|
||||
|| !parentClass.getInnerClasses().contains(classNode)) {
|
||||
return;
|
||||
}
|
||||
// TODO: calculate this constructor and other constructor usage
|
||||
Map<InsnArg, FieldNode> argsMap = getArgsToFieldsMapping(callMthNode, co);
|
||||
if (argsMap.isEmpty() && !callMthNode.getArguments(true).isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// all checks passed
|
||||
classNode.add(AFlag.ANONYMOUS_CLASS);
|
||||
callMthNode.add(AFlag.DONT_GENERATE);
|
||||
ClassNode classNode = callMthNode.getParentClass();
|
||||
if (!classNode.contains(AFlag.ANONYMOUS_CLASS)) {
|
||||
// check if class can be anonymous but not yet marked due to dependency issues
|
||||
if (!classNode.markAnonymousClass()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!mth.getParentClass().getInnerClasses().contains(classNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Map.Entry<InsnArg, FieldNode> entry : argsMap.entrySet()) {
|
||||
FieldNode field = entry.getValue();
|
||||
if (field == null) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.AttrList;
|
||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||
@@ -195,7 +196,13 @@ public class RegionUtils {
|
||||
return false;
|
||||
}
|
||||
if (container instanceof IBlock) {
|
||||
return !((IBlock) container).getInstructions().isEmpty();
|
||||
List<InsnNode> insnList = ((IBlock) container).getInstructions();
|
||||
for (InsnNode insnNode : insnList) {
|
||||
if (!insnNode.contains(AFlag.DONT_GENERATE)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if (container instanceof IRegion) {
|
||||
IRegion region = (IRegion) container;
|
||||
for (IContainer block : region.getSubBlocks()) {
|
||||
|
||||
Reference in New Issue
Block a user