Commit a684118d authored by Skylot's avatar Skylot

core: move field initialization from constructors if possible (#71)

parent a324376e
......@@ -8,6 +8,7 @@ import jadx.core.dex.visitors.DebugInfoVisitor;
import jadx.core.dex.visitors.DependencyCollector;
import jadx.core.dex.visitors.DotGraphVisitor;
import jadx.core.dex.visitors.EnumVisitor;
import jadx.core.dex.visitors.ExtractFieldInit;
import jadx.core.dex.visitors.FallbackModeVisitor;
import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.dex.visitors.MethodInlineVisitor;
......@@ -96,6 +97,7 @@ public class Jadx {
}
passes.add(new MethodInlineVisitor());
passes.add(new ExtractFieldInit());
passes.add(new ClassModifier());
passes.add(new EnumVisitor());
passes.add(new PrepareForCodeGen());
......
......@@ -15,8 +15,10 @@ import jadx.core.dex.instructions.mods.ConstructorInsn;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.DexNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.parser.FieldValueAttr;
import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.dex.nodes.parser.FieldInitAttr.InitType;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.CodegenException;
......@@ -339,13 +341,18 @@ public class ClassGen {
useType(code, f.getType());
code.add(' ');
code.add(f.getAlias());
FieldValueAttr fv = f.get(AType.FIELD_VALUE);
FieldInitAttr fv = f.get(AType.FIELD_INIT);
if (fv != null) {
code.add(" = ");
if (fv.getValue() == null) {
code.add(TypeGen.literalToString(0, f.getType()));
} else {
annotationGen.encodeValue(code, fv.getValue());
if (fv.getValueType() == InitType.CONST) {
annotationGen.encodeValue(code, fv.getValue());
} else if (fv.getValueType() == InitType.INSN) {
InsnGen insnGen = makeInsnGen(fv.getInsnMth());
addInsnBody(insnGen, code, fv.getInsn());
}
}
}
code.add(';');
......@@ -374,8 +381,7 @@ public class ClassGen {
ConstructorInsn constrInsn = f.getConstrInsn();
if (constrInsn.getArgsCount() > f.getStartArg()) {
if (igen == null) {
MethodGen mthGen = new MethodGen(this, enumFields.getStaticMethod());
igen = new InsnGen(mthGen, false);
igen = makeInsnGen(enumFields.getStaticMethod());
}
MethodNode callMth = cls.dex().resolveMethod(constrInsn.getCallMth());
igen.generateMethodArguments(code, constrInsn, f.getStartArg(), callMth);
......@@ -399,6 +405,19 @@ public class ClassGen {
}
}
private InsnGen makeInsnGen(MethodNode mth) {
MethodGen mthGen = new MethodGen(this, mth);
return new InsnGen(mthGen, false);
}
private void addInsnBody(InsnGen insnGen, CodeWriter code, InsnNode insn) {
try {
insnGen.makeInsn(insn, code, InsnGen.Flags.BODY_ONLY_NOWRAP);
} catch (Exception e) {
ErrorsCounter.classError(cls, "Failed to generate init code", e);
}
}
public void useType(CodeWriter code, ArgType type) {
PrimitiveType stype = type.getPrimitiveType();
if (stype == null) {
......
......@@ -15,7 +15,7 @@ import jadx.core.dex.nodes.IBlock;
import jadx.core.dex.nodes.IContainer;
import jadx.core.dex.nodes.IRegion;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.parser.FieldValueAttr;
import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.dex.regions.Region;
import jadx.core.dex.regions.SwitchRegion;
import jadx.core.dex.regions.SynchronizedRegion;
......@@ -249,7 +249,7 @@ public class RegionGen extends InsnGen {
} else {
staticField(code, fn.getFieldInfo());
// print original value, sometimes replace with incorrect field
FieldValueAttr valueAttr = fn.get(AType.FIELD_VALUE);
FieldInitAttr valueAttr = fn.get(AType.FIELD_INIT);
if (valueAttr != null && valueAttr.getValue() != null) {
code.add(" /*").add(valueAttr.getValue().toString()).add("*/");
}
......
......@@ -16,7 +16,7 @@ import jadx.core.dex.attributes.nodes.LoopLabelAttr;
import jadx.core.dex.attributes.nodes.MethodInlineAttr;
import jadx.core.dex.attributes.nodes.PhiListAttr;
import jadx.core.dex.attributes.nodes.SourceFileAttr;
import jadx.core.dex.nodes.parser.FieldValueAttr;
import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.dex.trycatch.CatchAttr;
import jadx.core.dex.trycatch.ExcHandlerAttr;
import jadx.core.dex.trycatch.SplitterBlockAttr;
......@@ -37,7 +37,7 @@ public class AType<T extends IAttribute> {
public static final AType<CatchAttr> CATCH_BLOCK = new AType<CatchAttr>();
public static final AType<SplitterBlockAttr> SPLITTER_BLOCK = new AType<SplitterBlockAttr>();
public static final AType<ForceReturnAttr> FORCE_RETURN = new AType<ForceReturnAttr>();
public static final AType<FieldValueAttr> FIELD_VALUE = new AType<FieldValueAttr>();
public static final AType<FieldInitAttr> FIELD_INIT = new AType<FieldInitAttr>();
public static final AType<FieldReplaceAttr> FIELD_REPLACE = new AType<FieldReplaceAttr>();
public static final AType<JadxErrorAttr> JADX_ERROR = new AType<JadxErrorAttr>();
public static final AType<MethodInlineAttr> METHOD_INLINE = new AType<MethodInlineAttr>();
......
......@@ -9,7 +9,7 @@ import jadx.core.dex.regions.conditions.IfCondition;
import jadx.core.utils.InsnUtils;
import jadx.core.utils.Utils;
import java.util.List;
import java.util.Collection;
public final class TernaryInsn extends InsnNode {
......@@ -54,7 +54,7 @@ public final class TernaryInsn extends InsnNode {
}
@Override
public void getRegisterArgs(List<RegisterArg> list) {
public void getRegisterArgs(Collection<RegisterArg> list) {
super.getRegisterArgs(list);
list.addAll(condition.getRegisterArgs());
}
......
......@@ -16,7 +16,8 @@ import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.nodes.parser.AnnotationsParser;
import jadx.core.dex.nodes.parser.FieldValueAttr;
import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.dex.nodes.parser.FieldInitAttr.InitType;
import jadx.core.dex.nodes.parser.SignatureParser;
import jadx.core.dex.nodes.parser.StaticValuesParser;
import jadx.core.utils.exceptions.DecodeException;
......@@ -155,25 +156,28 @@ public class ClassNode extends LineAttrNode implements ILoadable {
private void loadStaticValues(ClassDef cls, List<FieldNode> staticFields) throws DecodeException {
for (FieldNode f : staticFields) {
if (f.getAccessFlags().isFinal()) {
f.addAttr(new FieldValueAttr(null));
f.addAttr(FieldInitAttr.NULL_VALUE);
}
}
int offset = cls.getStaticValuesOffset();
if (offset != 0) {
StaticValuesParser parser = new StaticValuesParser(dex, dex.openSection(offset));
int count = parser.processFields(staticFields);
constFields = new LinkedHashMap<Object, FieldNode>(count);
for (FieldNode f : staticFields) {
AccessInfo accFlags = f.getAccessFlags();
if (accFlags.isStatic() && accFlags.isFinal()) {
FieldValueAttr fv = f.get(AType.FIELD_VALUE);
if (fv != null && fv.getValue() != null) {
if (accFlags.isPublic()) {
dex.getConstFields().put(fv.getValue(), f);
}
constFields.put(fv.getValue(), f);
if (offset == 0) {
return;
}
StaticValuesParser parser = new StaticValuesParser(dex, dex.openSection(offset));
int count = parser.processFields(staticFields);
if (count == 0) {
return;
}
constFields = new LinkedHashMap<Object, FieldNode>(count);
for (FieldNode f : staticFields) {
AccessInfo accFlags = f.getAccessFlags();
if (accFlags.isStatic() && accFlags.isFinal()) {
FieldInitAttr fv = f.get(AType.FIELD_INIT);
if (fv != null && fv.getValue() != null && fv.getValueType() == InitType.CONST) {
if (accFlags.isPublic()) {
dex.getConstFields().put(fv.getValue(), f);
}
constFields.put(fv.getValue(), f);
}
}
}
......@@ -442,6 +446,12 @@ public class ClassNode extends LineAttrNode implements ILoadable {
&& getDefaultConstructor() != null;
}
@Nullable
public MethodNode getClassInitMth() {
return searchMethodByName("<clinit>()V");
}
@Nullable
public MethodNode getDefaultConstructor() {
for (MethodNode mth : methods) {
if (mth.isDefaultConstructor()) {
......
......@@ -13,6 +13,7 @@ import jadx.core.utils.InsnUtils;
import jadx.core.utils.Utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
......@@ -157,12 +158,12 @@ public class InsnNode extends LineAttrNode {
this.offset = offset;
}
public void getRegisterArgs(List<RegisterArg> list) {
public void getRegisterArgs(Collection<RegisterArg> collection) {
for (InsnArg arg : this.getArguments()) {
if (arg.isRegister()) {
list.add((RegisterArg) arg);
collection.add((RegisterArg) arg);
} else if (arg.isInsnWrap()) {
((InsnWrapArg) arg).getWrapInsn().getRegisterArgs(list);
((InsnWrapArg) arg).getWrapInsn().getRegisterArgs(collection);
}
}
}
......@@ -235,8 +236,27 @@ public class InsnNode extends LineAttrNode {
if (this == other) {
return true;
}
return insnType == other.insnType
&& arguments.size() == other.arguments.size();
if (insnType != other.insnType
|| arguments.size() != other.arguments.size()) {
return false;
}
// check wrapped instructions
int size = arguments.size();
for (int i = 0; i < size; i++) {
InsnArg arg = arguments.get(i);
InsnArg otherArg = other.arguments.get(i);
if (arg.isInsnWrap()) {
if (!otherArg.isInsnWrap()) {
return false;
}
InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();
InsnNode otherWrapInsn = ((InsnWrapArg) otherArg).getWrapInsn();
if (!wrapInsn.isSame(otherWrapInsn)) {
return false;
}
}
}
return true;
}
protected <T extends InsnNode> T copyCommonParams(T copy) {
......
package jadx.core.dex.nodes.parser;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.IAttribute;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
public class FieldInitAttr implements IAttribute {
public static FieldInitAttr NULL_VALUE = constValue(null);
public enum InitType {
CONST,
INSN
}
private final Object value;
private final InitType valueType;
private final MethodNode insnMth;
private FieldInitAttr(InitType valueType, Object value, MethodNode insnMth) {
this.value = value;
this.valueType = valueType;
this.insnMth = insnMth;
}
public static FieldInitAttr constValue(Object value) {
return new FieldInitAttr(InitType.CONST, value, null);
}
public static FieldInitAttr insnValue(MethodNode mth, InsnNode insn) {
return new FieldInitAttr(InitType.INSN, insn, mth);
}
public Object getValue() {
return value;
}
public InsnNode getInsn() {
return (InsnNode) value;
}
public InitType getValueType() {
return valueType;
}
public MethodNode getInsnMth() {
return insnMth;
}
@Override
public AType<FieldInitAttr> getType() {
return AType.FIELD_INIT;
}
@Override
public String toString() {
return "V=" + value;
}
}
package jadx.core.dex.nodes.parser;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.IAttribute;
public class FieldValueAttr implements IAttribute {
private final Object value;
public FieldValueAttr(Object value) {
this.value = value;
}
@Override
public AType<FieldValueAttr> getType() {
return AType.FIELD_VALUE;
}
public Object getValue() {
return value;
}
@Override
public String toString() {
return "V=" + value;
}
}
......@@ -19,7 +19,9 @@ public class StaticValuesParser extends EncValueParser {
int count = Leb128.readUnsignedLeb128(in);
for (int i = 0; i < count; i++) {
Object value = parseValue();
fields.get(i).addAttr(new FieldValueAttr(value));
if (i < fields.size()) {
fields.get(i).addAttr(FieldInitAttr.constValue(value));
}
}
return count;
}
......
......@@ -18,6 +18,7 @@ import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.InstructionRemover;
import jadx.core.utils.exceptions.JadxException;
......@@ -45,8 +46,6 @@ public class ClassModifier extends AbstractVisitor {
removeSyntheticMethods(cls);
removeEmptyMethods(cls);
checkFieldsInit(cls);
markAnonymousClass(cls);
return false;
}
......@@ -182,55 +181,15 @@ public class ClassModifier extends AbstractVisitor {
// remove public empty constructors
if (af.isConstructor()
&& af.isPublic()
&& (af.isPublic() || af.isStatic())
&& mth.getArguments(false).isEmpty()
&& !mth.contains(AType.JADX_ERROR)) {
List<BlockNode> bb = mth.getBasicBlocks();
if (bb == null || bb.isEmpty() || allBlocksEmpty(bb)) {
if (bb == null || bb.isEmpty() || BlockUtils.isAllBlocksEmpty(bb)) {
mth.add(AFlag.DONT_GENERATE);
}
}
}
}
private static boolean allBlocksEmpty(List<BlockNode> blocks) {
for (BlockNode block : blocks) {
if (!block.getInstructions().isEmpty()) {
return false;
}
}
return true;
}
private static void checkFieldsInit(ClassNode cls) {
MethodNode clinit = cls.searchMethodByName("<clinit>()V");
if (clinit == null
|| !clinit.getAccessFlags().isStatic()
|| clinit.isNoCode()) {
return;
}
for (BlockNode block : clinit.getBasicBlocks()) {
for (InsnNode insn : block.getInstructions()) {
if (insn.getType() == InsnType.SPUT) {
processStaticFieldAssign(cls, (IndexInsnNode) insn);
}
}
}
}
/**
* Remove field initialization if it assign in "<clinit>" method
*/
private static void processStaticFieldAssign(ClassNode cls, IndexInsnNode insn) {
FieldInfo field = (FieldInfo) insn.getIndex();
String thisClass = cls.getClassInfo().getFullName();
if (field.getDeclClass().getFullName().equals(thisClass)) {
FieldNode fn = cls.searchField(field);
if (fn != null && fn.getAccessFlags().isFinal()) {
fn.remove(AType.FIELD_VALUE);
}
}
}
}
package jadx.core.dex.visitors;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.instructions.IndexInsnNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.InstructionRemover;
import jadx.core.utils.exceptions.JadxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@JadxVisitor(
name = "ExtractFieldInit",
desc = "Move duplicated field initialization from constructors",
runAfter = ModVisitor.class,
runBefore = ClassModifier.class
)
public class ExtractFieldInit extends AbstractVisitor {
@Override
public boolean visit(ClassNode cls) throws JadxException {
if (cls.isEnum()) {
return false;
}
for (ClassNode inner : cls.getInnerClasses()) {
visit(inner);
}
checkStaticFieldsInit(cls);
moveStaticFieldsInit(cls);
moveCommonFieldsInit(cls);
return false;
}
private static void checkStaticFieldsInit(ClassNode cls) {
MethodNode clinit = cls.getClassInitMth();
if (clinit == null
|| !clinit.getAccessFlags().isStatic()
|| clinit.isNoCode()) {
return;
}
for (BlockNode block : clinit.getBasicBlocks()) {
for (InsnNode insn : block.getInstructions()) {
if (insn.getType() == InsnType.SPUT) {
processStaticFieldAssign(cls, (IndexInsnNode) insn);
}
}
}
}
/**
* Remove final field in place initialization if it assign in class init method
*/
private static void processStaticFieldAssign(ClassNode cls, IndexInsnNode insn) {
FieldInfo field = (FieldInfo) insn.getIndex();
String thisClass = cls.getClassInfo().getFullName();
if (field.getDeclClass().getFullName().equals(thisClass)) {
FieldNode fn = cls.searchField(field);
if (fn != null && fn.getAccessFlags().isFinal()) {
fn.remove(AType.FIELD_INIT);
}
}
}
private static void moveStaticFieldsInit(ClassNode cls) {
MethodNode classInitMth = cls.getClassInitMth();
if (classInitMth == null) {
return;
}
for (FieldNode field : cls.getFields()) {
if (field.contains(AFlag.DONT_GENERATE)) {
continue;
}
if (field.getAccessFlags().isStatic()) {
List<InsnNode> initInsns = getFieldAssigns(classInitMth, field, InsnType.SPUT);
if (initInsns.size() == 1) {
InsnNode insn = initInsns.get(0);
if (checkInsn(insn)) {
InstructionRemover.remove(classInitMth, insn);
addFieldInitAttr(classInitMth, field, insn);
}
}
}
}
}
private static class InitInfo {
private final MethodNode constrMth;
private final List<InsnNode> putInsns = new ArrayList<InsnNode>();
private InitInfo(MethodNode constrMth) {
this.constrMth = constrMth;
}
public MethodNode getConstrMth() {
return constrMth;
}
public List<InsnNode> getPutInsns() {
return putInsns;
}
}
private static void moveCommonFieldsInit(ClassNode cls) {
List<MethodNode> constrList = getConstructorsList(cls);
if (constrList.isEmpty()) {
return;
}
List<InitInfo> infoList = new ArrayList<InitInfo>(constrList.size());
for (MethodNode constrMth : constrList) {
if (constrMth.isNoCode() || constrMth.getBasicBlocks().isEmpty()) {
return;
}
InitInfo info = new InitInfo(constrMth);
infoList.add(info);
// TODO: check not only first block
BlockNode blockNode = constrMth.getBasicBlocks().get(0);
for (InsnNode insn : blockNode.getInstructions()) {
if (insn.getType() == InsnType.IPUT && checkInsn(insn)) {
info.getPutInsns().add(insn);
} else if (!info.getPutInsns().isEmpty()) {
break;
}
}
}
// compare collected instructions
InitInfo common = null;
for (InitInfo info : infoList) {
if (common == null) {
common = info;
} else if (!compareInsns(common.getPutInsns(), info.getPutInsns())) {
return;
}
}
if (common == null) {
return;
}
Set<FieldInfo> fields = new HashSet<FieldInfo>();
for (InsnNode insn : common.getPutInsns()) {
FieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) insn).getIndex();
FieldNode field = cls.dex().resolveField(fieldInfo);
if (field == null) {
return;
}
if (!fields.add(fieldInfo)) {
return;
}
}
// all checks passed
for (InitInfo info : infoList) {
for (InsnNode putInsn : info.getPutInsns()) {
InstructionRemover.remove(info.getConstrMth(), putInsn);
}
}
for (InsnNode insn : common.getPutInsns()) {
FieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) insn).getIndex();
FieldNode field = cls.dex().resolveField(fieldInfo);
addFieldInitAttr(common.getConstrMth(), field, insn);
}
}
private static boolean compareInsns(List<InsnNode> base, List<InsnNode> other) {
if (base.size() != other.size()) {
return false;
}
int count = base.size();
for (int i = 0; i < count; i++) {
InsnNode baseInsn = base.get(i);
InsnNode otherInsn = other.get(i);
if (!baseInsn.isSame(otherInsn)) {
return false;
}
}
return true;
}
private static boolean checkInsn(InsnNode insn) {
Set<RegisterArg> regs = new HashSet<RegisterArg>();
insn.getRegisterArgs(regs);
if (regs.isEmpty()) {
return true;
}
for (RegisterArg reg : regs) {
if (!reg.isThis()) {
return false;
}
}
return true;
}
private static List<MethodNode> getConstructorsList(ClassNode cls) {
List<MethodNode> list = new ArrayList<MethodNode>();
for (MethodNode mth : cls.getMethods()) {
AccessInfo accFlags = mth.getAccessFlags();
if (!accFlags.isStatic() && accFlags.isConstructor()) {
list.add(mth);
if (BlockUtils.isAllBlocksEmpty(mth.getBasicBlocks())) {
return Collections.emptyList();
}
}
}
return list;
}
private static List<InsnNode> getFieldAssigns(MethodNode mth, FieldNode field, InsnType putInsn) {
if (mth.isNoCode()) {
return Collections.emptyList();
}
List<InsnNode> assignInsns = new ArrayList<InsnNode>();
for (BlockNode block : mth.getBasicBlocks()) {
for (InsnNode insn : block.getInstructions()) {
if (insn.getType() == putInsn) {
FieldInfo putNode = (FieldInfo) ((IndexInsnNode) insn).getIndex();
if (putNode.equals(field.getFieldInfo())) {
assignInsns.add(insn);
}
}
}
}
return assignInsns;
}
private static void addFieldInitAttr(MethodNode classInitMth, FieldNode field, InsnNode insn) {
InsnNode assignInsn = InsnNode.wrapArg(insn.getArg(0));
field.addAttr(FieldInitAttr.insnValue(classInitMth, assignInsn));
}
}
......@@ -531,4 +531,13 @@ public class BlockUtils {
}
return block;
}
public static boolean isAllBlocksEmpty(List<BlockNode> blocks) {
for (BlockNode block : blocks) {
if (!block.getInstructions().isEmpty()) {
return false;
}
}
return true;
}
}
......@@ -9,7 +9,7 @@ import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.nodes.DexNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.parser.FieldValueAttr;
import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.utils.exceptions.JadxRuntimeException;
import org.jetbrains.annotations.Nullable;
......@@ -82,7 +82,7 @@ public class InsnUtils {
FieldInfo f = (FieldInfo) ((IndexInsnNode) insn).getIndex();
FieldNode fieldNode = dex.resolveField(f);
if (fieldNode != null) {
FieldValueAttr attr = fieldNode.get(AType.FIELD_VALUE);
FieldInitAttr attr = fieldNode.get(AType.FIELD_INIT);
if (attr != null) {
return attr.getValue();
}
......
package jadx.tests.integration.others;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.junit.Test;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
public class TestFieldInit extends IntegrationTest {
public static class TestCls {
public class A {
}
private static List<String> s = new ArrayList<String>();
private A a = new A();
private int i = 1 + Random.class.getSimpleName().length();
private int n = 0;
public TestCls(int z) {
this.n = z;
this.n = 0;
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("List<String> s = new ArrayList"));
assertThat(code, containsOne("A a = new A();"));
assertThat(code, containsOne("int i = (Random.class.getSimpleName().length() + 1);"));
assertThat(code, containsOne("int n = 0;"));
assertThat(code, not(containsString("static {")));
assertThat(code, containsOne("this.n = z;"));
assertThat(code, containsOne("this.n = 0;"));
}
}
package jadx.tests.integration.others;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import org.junit.Test;
import static jadx.tests.api.utils.JadxMatchers.containsLines;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.junit.Assert.assertThat;
public class TestFieldInit2 extends IntegrationTest {
public static class TestCls {
public interface BasicAbstract {
void doSomething();
}
private BasicAbstract x = new BasicAbstract() {
@Override
public void doSomething() {
y = 1;
}
};
private int y = 0;
public TestCls() {
}
public TestCls(int z) {
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("x = new BasicAbstract() {"));
assertThat(code, containsOne("y = 0;"));
assertThat(code, containsLines(1, "public TestFieldInit2$TestCls(int z) {", "}"));
}
}
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