Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
J
jadx
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open-source
jadx
Commits
a684118d
Commit
a684118d
authored
Jul 01, 2015
by
Skylot
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
core: move field initialization from constructors if possible (#71)
parent
a324376e
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
495 additions
and
106 deletions
+495
-106
Jadx.java
jadx-core/src/main/java/jadx/core/Jadx.java
+2
-0
ClassGen.java
jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
+24
-5
RegionGen.java
jadx-core/src/main/java/jadx/core/codegen/RegionGen.java
+2
-2
AType.java
jadx-core/src/main/java/jadx/core/dex/attributes/AType.java
+2
-2
TernaryInsn.java
...ain/java/jadx/core/dex/instructions/mods/TernaryInsn.java
+2
-2
ClassNode.java
jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java
+26
-16
InsnNode.java
jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java
+25
-5
FieldInitAttr.java
...c/main/java/jadx/core/dex/nodes/parser/FieldInitAttr.java
+61
-0
FieldValueAttr.java
.../main/java/jadx/core/dex/nodes/parser/FieldValueAttr.java
+0
-27
StaticValuesParser.java
...n/java/jadx/core/dex/nodes/parser/StaticValuesParser.java
+3
-1
ClassModifier.java
...e/src/main/java/jadx/core/dex/visitors/ClassModifier.java
+3
-44
ExtractFieldInit.java
...rc/main/java/jadx/core/dex/visitors/ExtractFieldInit.java
+241
-0
BlockUtils.java
jadx-core/src/main/java/jadx/core/utils/BlockUtils.java
+9
-0
InsnUtils.java
jadx-core/src/main/java/jadx/core/utils/InsnUtils.java
+2
-2
TestFieldInit.java
...est/java/jadx/tests/integration/others/TestFieldInit.java
+49
-0
TestFieldInit2.java
...st/java/jadx/tests/integration/others/TestFieldInit2.java
+44
-0
No files found.
jadx-core/src/main/java/jadx/core/Jadx.java
View file @
a684118d
...
...
@@ -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
());
...
...
jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
View file @
a684118d
...
...
@@ -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
());
Field
ValueAttr
fv
=
f
.
get
(
AType
.
FIELD_VALUE
);
Field
InitAttr
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
)
{
...
...
jadx-core/src/main/java/jadx/core/codegen/RegionGen.java
View file @
a684118d
...
...
@@ -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.Field
Value
Attr
;
import
jadx.core.dex.nodes.parser.Field
Init
Attr
;
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
Field
ValueAttr
valueAttr
=
fn
.
get
(
AType
.
FIELD_VALUE
);
Field
InitAttr
valueAttr
=
fn
.
get
(
AType
.
FIELD_INIT
);
if
(
valueAttr
!=
null
&&
valueAttr
.
getValue
()
!=
null
)
{
code
.
add
(
" /*"
).
add
(
valueAttr
.
getValue
().
toString
()).
add
(
"*/"
);
}
...
...
jadx-core/src/main/java/jadx/core/dex/attributes/AType.java
View file @
a684118d
...
...
@@ -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.Field
Value
Attr
;
import
jadx.core.dex.nodes.parser.Field
Init
Attr
;
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
<
Field
ValueAttr
>
FIELD_VALUE
=
new
AType
<
FieldValue
Attr
>();
public
static
final
AType
<
Field
InitAttr
>
FIELD_INIT
=
new
AType
<
FieldInit
Attr
>();
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
>();
...
...
jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java
View file @
a684118d
...
...
@@ -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
());
}
...
...
jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java
View file @
a684118d
...
...
@@ -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
())
{
...
...
jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java
View file @
a684118d
...
...
@@ -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
)
{
...
...
jadx-core/src/main/java/jadx/core/dex/nodes/parser/FieldInitAttr.java
0 → 100644
View file @
a684118d
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
;
}
}
jadx-core/src/main/java/jadx/core/dex/nodes/parser/FieldValueAttr.java
deleted
100644 → 0
View file @
a324376e
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
;
}
}
jadx-core/src/main/java/jadx/core/dex/nodes/parser/StaticValuesParser.java
View file @
a684118d
...
...
@@ -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
;
}
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java
View file @
a684118d
...
...
@@ -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
()
||
a
llBlocksEmpty
(
bb
))
{
if
(
bb
==
null
||
bb
.
isEmpty
()
||
BlockUtils
.
isA
llBlocksEmpty
(
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
);
}
}
}
}
jadx-core/src/main/java/jadx/core/dex/visitors/ExtractFieldInit.java
0 → 100644
View file @
a684118d
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
));
}
}
jadx-core/src/main/java/jadx/core/utils/BlockUtils.java
View file @
a684118d
...
...
@@ -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
;
}
}
jadx-core/src/main/java/jadx/core/utils/InsnUtils.java
View file @
a684118d
...
...
@@ -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.Field
Value
Attr
;
import
jadx.core.dex.nodes.parser.Field
Init
Attr
;
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
)
{
Field
ValueAttr
attr
=
fieldNode
.
get
(
AType
.
FIELD_VALUE
);
Field
InitAttr
attr
=
fieldNode
.
get
(
AType
.
FIELD_INIT
);
if
(
attr
!=
null
)
{
return
attr
.
getValue
();
}
...
...
jadx-core/src/test/java/jadx/tests/integration/others/TestFieldInit.java
0 → 100644
View file @
a684118d
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;"
));
}
}
jadx-core/src/test/java/jadx/tests/integration/others/TestFieldInit2.java
0 → 100644
View file @
a684118d
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) {"
,
"}"
));
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment