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
9b091b7c
Commit
9b091b7c
authored
Jan 13, 2019
by
Skylot
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: reimplement variable declaration visitor
parent
7b14e322
Hide whitespace changes
Inline
Side-by-side
Showing
53 changed files
with
1043 additions
and
539 deletions
+1043
-539
Jadx.java
jadx-core/src/main/java/jadx/core/Jadx.java
+3
-4
ClsSet.java
jadx-core/src/main/java/jadx/core/clsp/ClsSet.java
+7
-1
InsnGen.java
jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
+8
-3
MethodGen.java
jadx-core/src/main/java/jadx/core/codegen/MethodGen.java
+28
-23
NameGen.java
jadx-core/src/main/java/jadx/core/codegen/NameGen.java
+33
-32
RegionGen.java
jadx-core/src/main/java/jadx/core/codegen/RegionGen.java
+4
-2
AFlag.java
jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java
+2
-0
AttrList.java
...core/src/main/java/jadx/core/dex/attributes/AttrList.java
+1
-1
IAttribute.java
...re/src/main/java/jadx/core/dex/attributes/IAttribute.java
+1
-1
DeclareVariablesAttr.java
.../jadx/core/dex/attributes/nodes/DeclareVariablesAttr.java
+5
-5
CodeVar.java
...rc/main/java/jadx/core/dex/instructions/args/CodeVar.java
+96
-0
RegisterArg.java
...ain/java/jadx/core/dex/instructions/args/RegisterArg.java
+5
-1
SSAVar.java
...src/main/java/jadx/core/dex/instructions/args/SSAVar.java
+36
-16
MethodNode.java
jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java
+3
-1
ForEachLoop.java
...rc/main/java/jadx/core/dex/regions/loops/ForEachLoop.java
+3
-0
ModVisitor.java
...core/src/main/java/jadx/core/dex/visitors/ModVisitor.java
+1
-0
PrepareForCodeGen.java
...c/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java
+4
-3
BlockExceptionHandler.java
.../core/dex/visitors/blocksmaker/BlockExceptionHandler.java
+1
-0
LoopRegionVisitor.java
...ava/jadx/core/dex/visitors/regions/LoopRegionVisitor.java
+27
-15
ProcessVariables.java
...java/jadx/core/dex/visitors/regions/ProcessVariables.java
+0
-331
RegionMakerVisitor.java
...va/jadx/core/dex/visitors/regions/RegionMakerVisitor.java
+1
-1
CollectUsageRegionVisitor.java
...visitors/regions/variables/CollectUsageRegionVisitor.java
+87
-0
ProcessVariables.java
...core/dex/visitors/regions/variables/ProcessVariables.java
+283
-0
UsePlace.java
...va/jadx/core/dex/visitors/regions/variables/UsePlace.java
+50
-0
VarUsage.java
...va/jadx/core/dex/visitors/regions/variables/VarUsage.java
+33
-0
EliminatePhiNodes.java
...in/java/jadx/core/dex/visitors/ssa/EliminatePhiNodes.java
+64
-0
SSATransform.java
...rc/main/java/jadx/core/dex/visitors/ssa/SSATransform.java
+0
-1
ITypeListener.java
...a/jadx/core/dex/visitors/typeinference/ITypeListener.java
+3
-1
TypeInferenceVisitor.java
...core/dex/visitors/typeinference/TypeInferenceVisitor.java
+7
-3
TypeUpdate.java
...java/jadx/core/dex/visitors/typeinference/TypeUpdate.java
+37
-30
DebugUtils.java
jadx-core/src/main/java/jadx/core/utils/DebugUtils.java
+8
-0
StringUtils.java
jadx-core/src/main/java/jadx/core/utils/StringUtils.java
+4
-0
TypeCompareTest.java
...jadx/core/dex/visitors/typeinference/TypeCompareTest.java
+3
-0
BaseExternalTest.java
...e/src/test/java/jadx/tests/external/BaseExternalTest.java
+2
-0
TestReturnWrapping.java
.../test/java/jadx/tests/integration/TestReturnWrapping.java
+5
-1
TestStringBuilderElimination2.java
...jadx/tests/integration/TestStringBuilderElimination2.java
+10
-18
TestArith.java
...src/test/java/jadx/tests/integration/arith/TestArith.java
+22
-7
TestArrays3.java
.../test/java/jadx/tests/integration/arrays/TestArrays3.java
+13
-1
TestGenerics2.java
...t/java/jadx/tests/integration/generics/TestGenerics2.java
+11
-0
TestInlineInLoop.java
.../java/jadx/tests/integration/inline/TestInlineInLoop.java
+7
-7
TestArrayForEach2.java
.../java/jadx/tests/integration/loops/TestArrayForEach2.java
+4
-0
TestIndexForLoop.java
...t/java/jadx/tests/integration/loops/TestIndexForLoop.java
+8
-0
TestNameAssign2.java
...st/java/jadx/tests/integration/names/TestNameAssign2.java
+3
-3
TestSwitchReturnFromCase.java
.../tests/integration/switches/TestSwitchReturnFromCase.java
+9
-4
TestSynchronized.java
.../jadx/tests/integration/synchronize/TestSynchronized.java
+5
-4
TestTryCatch3.java
...t/java/jadx/tests/integration/trycatch/TestTryCatch3.java
+1
-1
TestTryCatch7.java
...t/java/jadx/tests/integration/trycatch/TestTryCatch7.java
+1
-1
TestTryCatchMultiException.java
...ests/integration/trycatch/TestTryCatchMultiException.java
+36
-0
TestArrayTypes.java
...est/java/jadx/tests/integration/types/TestArrayTypes.java
+44
-0
TestTypeResolver5.java
.../java/jadx/tests/integration/types/TestTypeResolver5.java
+1
-14
TestVariables2.java
...java/jadx/tests/integration/variables/TestVariables2.java
+9
-0
TestVariablesDefinitions.java
...tests/integration/variables/TestVariablesDefinitions.java
+2
-1
TestTypeResolver5.smali
jadx-core/src/test/smali/types/TestTypeResolver5.smali
+2
-2
No files found.
jadx-core/src/main/java/jadx/core/Jadx.java
View file @
9b091b7c
...
...
@@ -36,9 +36,9 @@ import jadx.core.dex.visitors.debuginfo.DebugInfoParseVisitor;
import
jadx.core.dex.visitors.regions.CheckRegions
;
import
jadx.core.dex.visitors.regions.IfRegionVisitor
;
import
jadx.core.dex.visitors.regions.LoopRegionVisitor
;
import
jadx.core.dex.visitors.regions.ProcessVariables
;
import
jadx.core.dex.visitors.regions.RegionMakerVisitor
;
import
jadx.core.dex.visitors.regions.ReturnVisitor
;
import
jadx.core.dex.visitors.regions.variables.ProcessVariables
;
import
jadx.core.dex.visitors.ssa.EliminatePhiNodes
;
import
jadx.core.dex.visitors.ssa.SSATransform
;
import
jadx.core.dex.visitors.typeinference.TypeInferenceVisitor
;
...
...
@@ -97,16 +97,15 @@ public class Jadx {
passes
.
add
(
new
ExtractFieldInit
());
passes
.
add
(
new
ClassModifier
());
passes
.
add
(
new
EnumVisitor
());
passes
.
add
(
new
PrepareForCodeGen
());
passes
.
add
(
new
LoopRegionVisitor
());
passes
.
add
(
new
ProcessVariables
());
passes
.
add
(
new
ProcessVariables
());
passes
.
add
(
new
PrepareForCodeGen
());
if
(
args
.
isCfgOutput
())
{
passes
.
add
(
DotGraphVisitor
.
dumpRegions
());
}
passes
.
add
(
new
DependencyCollector
());
passes
.
add
(
new
RenameVisitor
());
}
return
passes
;
...
...
jadx-core/src/main/java/jadx/core/clsp/ClsSet.java
View file @
9b091b7c
...
...
@@ -45,6 +45,8 @@ public class ClsSet {
private
static
final
String
STRING_CHARSET
=
"US-ASCII"
;
private
static
final
NClass
[]
EMPTY_NCLASS_ARRAY
=
new
NClass
[
0
];
private
NClass
[]
classes
;
public
void
load
(
RootNode
root
)
{
...
...
@@ -93,7 +95,11 @@ public class ClsSet {
parents
.
add
(
c
);
}
}
return
parents
.
toArray
(
new
NClass
[
parents
.
size
()]);
int
size
=
parents
.
size
();
if
(
size
==
0
)
{
return
EMPTY_NCLASS_ARRAY
;
}
return
parents
.
toArray
(
new
NClass
[
size
]);
}
private
static
NClass
getCls
(
String
fullName
,
Map
<
String
,
NClass
>
names
)
{
...
...
jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
View file @
9b091b7c
...
...
@@ -33,6 +33,7 @@ import jadx.core.dex.instructions.InvokeType;
import
jadx.core.dex.instructions.NewArrayNode
;
import
jadx.core.dex.instructions.SwitchNode
;
import
jadx.core.dex.instructions.args.ArgType
;
import
jadx.core.dex.instructions.args.CodeVar
;
import
jadx.core.dex.instructions.args.FieldArg
;
import
jadx.core.dex.instructions.args.InsnArg
;
import
jadx.core.dex.instructions.args.InsnWrapArg
;
...
...
@@ -122,12 +123,16 @@ public class InsnGen {
}
public
void
declareVar
(
CodeWriter
code
,
RegisterArg
arg
)
{
if
(
arg
.
getSVar
().
contains
(
AFlag
.
FINAL
))
{
declareVar
(
code
,
arg
.
getSVar
().
getCodeVar
());
}
public
void
declareVar
(
CodeWriter
code
,
CodeVar
codeVar
)
{
if
(
codeVar
.
isFinal
())
{
code
.
add
(
"final "
);
}
useType
(
code
,
arg
.
getType
());
useType
(
code
,
codeVar
.
getType
());
code
.
add
(
' '
);
code
.
add
(
mgen
.
getNameGen
().
assignArg
(
arg
));
code
.
add
(
mgen
.
getNameGen
().
assignArg
(
codeVar
));
}
private
String
lit
(
LiteralArg
arg
)
{
...
...
jadx-core/src/main/java/jadx/core/codegen/MethodGen.java
View file @
9b091b7c
...
...
@@ -4,8 +4,6 @@ import java.util.Iterator;
import
java.util.List
;
import
com.android.dx.rop.code.AccessFlags
;
import
jadx.core.dex.info.ClassInfo
;
import
jadx.core.utils.Utils
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
...
...
@@ -13,8 +11,10 @@ import jadx.core.dex.attributes.AFlag;
import
jadx.core.dex.attributes.AType
;
import
jadx.core.dex.attributes.annotations.MethodParameters
;
import
jadx.core.dex.info.AccessInfo
;
import
jadx.core.dex.info.ClassInfo
;
import
jadx.core.dex.instructions.InsnType
;
import
jadx.core.dex.instructions.args.ArgType
;
import
jadx.core.dex.instructions.args.CodeVar
;
import
jadx.core.dex.instructions.args.RegisterArg
;
import
jadx.core.dex.instructions.args.SSAVar
;
import
jadx.core.dex.nodes.InsnNode
;
...
...
@@ -22,8 +22,8 @@ import jadx.core.dex.nodes.MethodNode;
import
jadx.core.dex.trycatch.CatchAttr
;
import
jadx.core.dex.visitors.DepthTraversal
;
import
jadx.core.dex.visitors.FallbackModeVisitor
;
import
jadx.core.utils.ErrorsCounter
;
import
jadx.core.utils.InsnUtils
;
import
jadx.core.utils.Utils
;
import
jadx.core.utils.exceptions.CodegenException
;
import
jadx.core.utils.exceptions.DecodeException
;
...
...
@@ -108,10 +108,7 @@ public class MethodGen {
}
else
if
(
args
.
size
()
>
2
)
{
args
=
args
.
subList
(
2
,
args
.
size
());
}
else
{
LOG
.
warn
(
ErrorsCounter
.
formatMsg
(
mth
,
"Incorrect number of args for enum constructor: "
+
args
.
size
()
+
" (expected >= 2)"
));
mth
.
addComment
(
"JADX WARN: Incorrect number of args for enum constructor: "
+
args
.
size
()
+
" (expected >= 2)"
);
}
}
addMethodArguments
(
code
,
args
);
...
...
@@ -121,40 +118,48 @@ public class MethodGen {
return
true
;
}
private
void
addMethodArguments
(
CodeWriter
argsC
ode
,
List
<
RegisterArg
>
args
)
{
private
void
addMethodArguments
(
CodeWriter
c
ode
,
List
<
RegisterArg
>
args
)
{
MethodParameters
paramsAnnotation
=
mth
.
get
(
AType
.
ANNOTATION_MTH_PARAMETERS
);
int
i
=
0
;
for
(
Iterator
<
RegisterArg
>
it
=
args
.
iterator
();
it
.
hasNext
();
)
{
RegisterArg
arg
=
it
.
next
();
ArgType
argType
=
arg
.
getInitType
();
Iterator
<
RegisterArg
>
it
=
args
.
iterator
();
while
(
it
.
hasNext
())
{
RegisterArg
mthArg
=
it
.
next
();
SSAVar
ssaVar
=
mthArg
.
getSVar
();
CodeVar
var
;
if
(
ssaVar
==
null
)
{
// null for abstract or interface methods
var
=
CodeVar
.
fromMthArg
(
mthArg
);
}
else
{
var
=
ssaVar
.
getCodeVar
();
}
ArgType
argType
=
var
.
getType
();
// add argument annotation
if
(
paramsAnnotation
!=
null
)
{
annotationGen
.
addForParameter
(
argsC
ode
,
paramsAnnotation
,
i
);
annotationGen
.
addForParameter
(
c
ode
,
paramsAnnotation
,
i
);
}
SSAVar
argSVar
=
arg
.
getSVar
();
if
(
argSVar
!=
null
&&
argSVar
.
contains
(
AFlag
.
FINAL
))
{
argsCode
.
add
(
"final "
);
if
(
var
.
isFinal
())
{
code
.
add
(
"final "
);
}
if
(!
it
.
hasNext
()
&&
mth
.
getAccessFlags
().
isVarArgs
())
{
// change last array argument to varargs
if
(
argType
.
isArray
())
{
ArgType
elType
=
argType
.
getArrayElement
();
classGen
.
useType
(
argsC
ode
,
elType
);
argsC
ode
.
add
(
"..."
);
classGen
.
useType
(
c
ode
,
elType
);
c
ode
.
add
(
"..."
);
}
else
{
LOG
.
warn
(
ErrorsCounter
.
formatMsg
(
mth
,
"Last argument in varargs method not array"
)
);
classGen
.
useType
(
argsC
ode
,
argType
);
mth
.
addComment
(
"JADX INFO: Last argument in varargs method is not array: "
+
var
);
classGen
.
useType
(
c
ode
,
argType
);
}
}
else
{
classGen
.
useType
(
argsC
ode
,
argType
);
classGen
.
useType
(
c
ode
,
argType
);
}
argsC
ode
.
add
(
' '
);
argsCode
.
add
(
nameGen
.
assignArg
(
arg
));
c
ode
.
add
(
' '
);
code
.
add
(
nameGen
.
assignArg
(
var
));
i
++;
if
(
it
.
hasNext
())
{
argsC
ode
.
add
(
", "
);
c
ode
.
add
(
", "
);
}
}
}
...
...
jadx-core/src/main/java/jadx/core/codegen/NameGen.java
View file @
9b091b7c
package
jadx
.
core
.
codegen
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
...
...
@@ -11,6 +12,7 @@ import jadx.core.dex.info.ClassInfo;
import
jadx.core.dex.info.MethodInfo
;
import
jadx.core.dex.instructions.InvokeNode
;
import
jadx.core.dex.instructions.args.ArgType
;
import
jadx.core.dex.instructions.args.CodeVar
;
import
jadx.core.dex.instructions.args.InsnArg
;
import
jadx.core.dex.instructions.args.InsnWrapArg
;
import
jadx.core.dex.instructions.args.NamedArg
;
...
...
@@ -45,7 +47,8 @@ public class NameGen {
"java.lang.Float"
,
"f"
,
"java.lang.Long"
,
"l"
,
"java.lang.Double"
,
"d"
,
"java.lang.StringBuilder"
,
"sb"
"java.lang.StringBuilder"
,
"sb"
,
"java.lang.Exception"
,
"exc"
);
}
...
...
@@ -54,13 +57,13 @@ public class NameGen {
this
.
fallback
=
fallback
;
}
public
String
assignArg
(
RegisterArg
arg
)
{
String
name
=
makeArgName
(
arg
);
public
String
assignArg
(
CodeVar
var
)
{
String
name
=
makeArgName
(
var
);
if
(
fallback
)
{
return
name
;
}
name
=
getUniqueVarName
(
name
);
arg
.
setName
(
name
);
var
.
setName
(
name
);
return
name
;
}
...
...
@@ -100,52 +103,50 @@ public class NameGen {
return
r
;
}
private
String
makeArgName
(
RegisterArg
arg
)
{
private
String
makeArgName
(
CodeVar
var
)
{
if
(
fallback
)
{
return
getFallbackName
(
arg
);
return
getFallbackName
(
var
);
}
if
(
arg
.
isThis
())
{
if
(
var
.
isThis
())
{
return
RegisterArg
.
THIS_ARG_NAME
;
}
String
name
=
arg
.
getName
();
String
varName
=
name
!=
null
?
name
:
guessName
(
arg
);
String
name
=
var
.
getName
();
String
varName
=
name
!=
null
?
name
:
guessName
(
var
);
if
(
NameMapper
.
isReserved
(
varName
))
{
varName
=
varName
+
"R"
;
}
if
(!
NameMapper
.
isValidIdentifier
(
varName
))
{
varName
=
getFallbackName
(
arg
);
varName
=
getFallbackName
(
var
);
}
return
varName
;
}
private
String
getFallbackName
(
CodeVar
var
)
{
return
getFallbackName
(
var
.
getSsaVars
().
get
(
0
).
getAssign
());
}
private
String
getFallbackName
(
RegisterArg
arg
)
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
'r'
).
append
(
arg
.
getRegNum
());
SSAVar
sVar
=
arg
.
getSVar
();
if
(
sVar
!=
null
)
{
sb
.
append
(
'v'
).
append
(
sVar
.
getVersion
());
}
return
sb
.
toString
();
return
"r"
+
arg
.
getRegNum
();
}
private
String
guessName
(
RegisterArg
arg
)
{
SSAVar
sVar
=
arg
.
getSVar
();
if
(
sVar
!=
null
&&
sVar
.
getName
()
==
null
)
{
RegisterArg
assignArg
=
sVar
.
getAssign
();
InsnNode
assignInsn
=
assignArg
.
getParentInsn
();
if
(
assignInsn
!=
null
)
{
String
name
=
makeNameFromInsn
(
assignInsn
);
if
(
name
!=
null
&&
!
NameMapper
.
isReserved
(
name
))
{
assignArg
.
setName
(
name
);
return
name
;
private
String
guessName
(
CodeVar
var
)
{
List
<
SSAVar
>
ssaVars
=
var
.
getSsaVars
();
if
(
ssaVars
!=
null
&&
!
ssaVars
.
isEmpty
())
{
// TODO: use all vars for better name generation
SSAVar
ssaVar
=
ssaVars
.
get
(
0
);
if
(
ssaVar
!=
null
&&
ssaVar
.
getName
()
==
null
)
{
RegisterArg
assignArg
=
ssaVar
.
getAssign
();
InsnNode
assignInsn
=
assignArg
.
getParentInsn
();
if
(
assignInsn
!=
null
)
{
String
name
=
makeNameFromInsn
(
assignInsn
);
if
(
name
!=
null
&&
!
NameMapper
.
isReserved
(
name
))
{
assignArg
.
setName
(
name
);
return
name
;
}
}
}
}
ArgType
type
=
arg
.
getType
();
if
(!
type
.
isTypeKnown
()
&&
arg
.
getInitType
().
isTypeKnown
())
{
type
=
arg
.
getInitType
();
}
return
makeNameForType
(
type
);
return
makeNameForType
(
var
.
getType
());
}
private
String
makeNameForType
(
ArgType
type
)
{
...
...
jadx-core/src/main/java/jadx/core/codegen/RegionGen.java
View file @
9b091b7c
...
...
@@ -14,6 +14,7 @@ import jadx.core.dex.attributes.nodes.ForceReturnAttr;
import
jadx.core.dex.attributes.nodes.LoopLabelAttr
;
import
jadx.core.dex.info.ClassInfo
;
import
jadx.core.dex.instructions.SwitchNode
;
import
jadx.core.dex.instructions.args.CodeVar
;
import
jadx.core.dex.instructions.args.InsnArg
;
import
jadx.core.dex.instructions.args.NamedArg
;
import
jadx.core.dex.instructions.args.RegisterArg
;
...
...
@@ -75,7 +76,7 @@ public class RegionGen extends InsnGen {
private
void
declareVars
(
CodeWriter
code
,
IContainer
cont
)
{
DeclareVariablesAttr
declVars
=
cont
.
get
(
AType
.
DECLARE_VARIABLES
);
if
(
declVars
!=
null
)
{
for
(
RegisterArg
v
:
declVars
.
getVars
())
{
for
(
CodeVar
v
:
declVars
.
getVars
())
{
code
.
startLine
();
declareVar
(
code
,
v
);
code
.
add
(
';'
);
...
...
@@ -323,7 +324,8 @@ public class RegionGen extends InsnGen {
code
.
add
(
' '
);
InsnArg
arg
=
handler
.
getArg
();
if
(
arg
instanceof
RegisterArg
)
{
code
.
add
(
mgen
.
getNameGen
().
assignArg
((
RegisterArg
)
arg
));
RegisterArg
reg
=
(
RegisterArg
)
arg
;
code
.
add
(
mgen
.
getNameGen
().
assignArg
(
reg
.
getSVar
().
getCodeVar
()));
}
else
if
(
arg
instanceof
NamedArg
)
{
code
.
add
(
mgen
.
getNameGen
().
assignNamedArg
((
NamedArg
)
arg
));
}
...
...
jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java
View file @
9b091b7c
...
...
@@ -26,6 +26,8 @@ public enum AFlag {
ANONYMOUS_CONSTRUCTOR
,
ANONYMOUS_CLASS
,
THIS
,
METHOD_ARGUMENT
,
// RegisterArg attribute for method arguments
CUSTOM_DECLARE
,
// variable for this register don't need declaration
ELSE_IF_CHAIN
,
...
...
jadx-core/src/main/java/jadx/core/dex/attributes/AttrList.java
View file @
9b091b7c
...
...
@@ -25,6 +25,6 @@ public class AttrList<T> implements IAttribute {
@Override
public
String
toString
()
{
return
Utils
.
listToString
(
list
);
return
Utils
.
listToString
(
list
,
"\n"
);
}
}
jadx-core/src/main/java/jadx/core/dex/attributes/IAttribute.java
View file @
9b091b7c
package
jadx
.
core
.
dex
.
attributes
;
public
interface
IAttribute
{
<
T
extends
IAttribute
>
AType
<
T
>
getType
();
AType
<?
extends
IAttribute
>
getType
();
}
jadx-core/src/main/java/jadx/core/dex/attributes/nodes/DeclareVariablesAttr.java
View file @
9b091b7c
package
jadx
.
core
.
dex
.
attributes
.
nodes
;
import
java.util.
Linked
List
;
import
java.util.
Array
List
;
import
java.util.List
;
import
jadx.core.dex.attributes.AType
;
import
jadx.core.dex.attributes.IAttribute
;
import
jadx.core.dex.instructions.args.
RegisterArg
;
import
jadx.core.dex.instructions.args.
CodeVar
;
import
jadx.core.utils.Utils
;
/**
...
...
@@ -13,13 +13,13 @@ import jadx.core.utils.Utils;
*/
public
class
DeclareVariablesAttr
implements
IAttribute
{
private
final
List
<
RegisterArg
>
vars
=
new
Linked
List
<>();
private
final
List
<
CodeVar
>
vars
=
new
Array
List
<>();
public
Iterable
<
RegisterArg
>
getVars
()
{
public
Iterable
<
CodeVar
>
getVars
()
{
return
vars
;
}
public
void
addVar
(
RegisterArg
arg
)
{
public
void
addVar
(
CodeVar
arg
)
{
vars
.
add
(
arg
);
}
...
...
jadx-core/src/main/java/jadx/core/dex/instructions/args/CodeVar.java
0 → 100644
View file @
9b091b7c
package
jadx
.
core
.
dex
.
instructions
.
args
;
import
java.util.Collections
;
import
java.util.List
;
public
class
CodeVar
{
private
String
name
;
private
ArgType
type
;
private
List
<
SSAVar
>
ssaVars
;
private
boolean
isFinal
;
private
boolean
isThis
;
private
boolean
isDeclared
;
public
static
CodeVar
fromMthArg
(
RegisterArg
mthArg
)
{
CodeVar
var
=
new
CodeVar
();
var
.
setType
(
mthArg
.
getInitType
());
var
.
setName
(
mthArg
.
getName
());
var
.
setDeclared
(
true
);
var
.
setThis
(
mthArg
.
isThis
());
var
.
setSsaVars
(
Collections
.
singletonList
(
new
SSAVar
(
mthArg
.
getRegNum
(),
0
,
mthArg
)));
return
var
;
}
public
String
getName
()
{
return
name
;
}
public
void
setName
(
String
name
)
{
this
.
name
=
name
;
}
public
ArgType
getType
()
{
return
type
;
}
public
void
setType
(
ArgType
type
)
{
this
.
type
=
type
;
}
public
List
<
SSAVar
>
getSsaVars
()
{
return
ssaVars
;
}
public
void
setSsaVars
(
List
<
SSAVar
>
ssaVars
)
{
if
(
ssaVars
.
size
()
==
1
)
{
this
.
ssaVars
=
Collections
.
singletonList
(
ssaVars
.
get
(
0
));
}
else
{
this
.
ssaVars
=
ssaVars
;
}
}
public
boolean
isFinal
()
{
return
isFinal
;
}
public
void
setFinal
(
boolean
aFinal
)
{
isFinal
=
aFinal
;
}
public
boolean
isThis
()
{
return
isThis
;
}
public
void
setThis
(
boolean
aThis
)
{
isThis
=
aThis
;
}
public
boolean
isDeclared
()
{
return
isDeclared
;
}
public
void
setDeclared
(
boolean
declared
)
{
isDeclared
=
declared
;
}
/**
* Merge flags with OR operator
*/
public
void
mergeFlagsFrom
(
CodeVar
other
)
{
if
(
other
.
isDeclared
())
{
setDeclared
(
true
);
}
if
(
other
.
isThis
())
{
setThis
(
true
);
}
if
(
other
.
isFinal
())
{
setFinal
(
true
);
}
}
@Override
public
String
toString
()
{
return
(
isFinal
?
"final "
:
""
)
+
type
+
" "
+
name
;
}
}
jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java
View file @
9b091b7c
...
...
@@ -38,7 +38,7 @@ public class RegisterArg extends InsnArg implements Named {
@Override
public
void
setType
(
ArgType
type
)
{
if
(
sVar
!=
null
)
{
sVar
.
getTypeInfo
().
setType
(
type
);
sVar
.
setType
(
type
);
}
}
...
...
@@ -167,6 +167,10 @@ public class RegisterArg extends InsnArg implements Named {
return
regNum
==
arg
.
regNum
&&
type
.
equals
(
arg
.
type
);
}
public
boolean
sameCodeVar
(
RegisterArg
arg
)
{
return
this
.
getSVar
().
getCodeVar
()
==
arg
.
getSVar
().
getCodeVar
();
}
@Override
public
int
hashCode
()
{
return
regNum
;
...
...
jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java
View file @
9b091b7c
...
...
@@ -15,19 +15,22 @@ import jadx.core.dex.attributes.nodes.RegDebugInfoAttr;
import
jadx.core.dex.instructions.PhiInsn
;
import
jadx.core.dex.nodes.MethodNode
;
import
jadx.core.dex.visitors.typeinference.TypeInfo
;
import
jadx.core.utils.StringUtils
;
import
jadx.core.utils.exceptions.JadxRuntimeException
;
public
class
SSAVar
extends
AttrNode
{
private
final
int
regNum
;
private
final
int
version
;
@NotNull
private
RegisterArg
assign
;
private
final
List
<
RegisterArg
>
useList
=
new
ArrayList
<>(
2
);
@Nullable
private
PhiInsn
usedInPhi
;
private
TypeInfo
typeInfo
=
new
TypeInfo
();
private
VarName
varName
;
@Nullable
(
"Set in EliminatePhiNodes pass"
)
private
CodeVar
codeVar
;
public
SSAVar
(
int
regNum
,
int
v
,
@NotNull
RegisterArg
assign
)
{
this
.
regNum
=
regNum
;
...
...
@@ -62,6 +65,13 @@ public class SSAVar extends AttrNode {
return
useList
.
size
();
}
public
void
setType
(
ArgType
type
)
{
typeInfo
.
setType
(
type
);
if
(
codeVar
!=
null
)
{
codeVar
.
setType
(
type
);
}
}
public
void
use
(
RegisterArg
arg
)
{
if
(
arg
.
getSVar
()
!=
null
)
{
arg
.
getSVar
().
removeUse
(
arg
);
...
...
@@ -101,34 +111,38 @@ public class SSAVar extends AttrNode {
public
void
setName
(
String
name
)
{
if
(
name
!=
null
)
{
if
(
varName
==
null
)
{
varName
=
new
VarName
(
);
if
(
codeVar
==
null
)
{
throw
new
JadxRuntimeException
(
"CodeVar not initialized for name set in SSAVar: "
+
this
);
}
varName
.
setName
(
name
);
codeVar
.
setName
(
name
);
}
}
public
String
getName
()
{
if
(
varName
==
null
)
{
if
(
codeVar
==
null
)
{
return
null
;
}
return
varName
.
getName
();
return
codeVar
.
getName
();
}
public
VarName
getVarName
()
{
return
varName
;
public
TypeInfo
getTypeInfo
()
{
return
typeInfo
;
}
public
void
setVarName
(
VarName
varName
)
{
this
.
varName
=
varName
;
@NotNull
public
CodeVar
getCodeVar
()
{
if
(
codeVar
==
null
)
{
throw
new
JadxRuntimeException
(
"Code variable not set in "
+
this
);
}
return
codeVar
;
}
public
TypeInfo
getTypeInfo
(
)
{
return
typeInfo
;
public
void
setCodeVar
(
@NotNull
CodeVar
codeVar
)
{
this
.
codeVar
=
codeVar
;
}
public
void
setTypeInfo
(
TypeInfo
typeInfo
)
{
this
.
typeInfo
=
typeInfo
;
public
boolean
isCodeVarSet
(
)
{
return
codeVar
!=
null
;
}
@Override
...
...
@@ -148,9 +162,15 @@ public class SSAVar extends AttrNode {
return
31
*
regNum
+
version
;
}
public
String
toShortString
()
{
return
"r"
+
regNum
+
":"
+
version
;
}
@Override
public
String
toString
()
{
return
"r"
+
regNum
+
":"
+
version
+
" "
+
typeInfo
.
getType
();
return
toShortString
()
+
(
StringUtils
.
notEmpty
(
getName
())
?
" '"
+
getName
()
+
"' "
:
""
)
+
" "
+
typeInfo
.
getType
();
}
public
String
getDetailedVarInfo
(
MethodNode
mth
)
{
...
...
jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java
View file @
9b091b7c
...
...
@@ -230,7 +230,9 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
}
argsList
=
new
ArrayList
<>(
args
.
size
());
for
(
ArgType
arg
:
args
)
{
argsList
.
add
(
InsnArg
.
typeImmutableReg
(
pos
,
arg
));
TypeImmutableArg
regArg
=
InsnArg
.
typeImmutableReg
(
pos
,
arg
);
regArg
.
add
(
AFlag
.
METHOD_ARGUMENT
);
argsList
.
add
(
regArg
);
pos
+=
arg
.
getRegCount
();
}
}
...
...
jadx-core/src/main/java/jadx/core/dex/regions/loops/ForEachLoop.java
View file @
9b091b7c
...
...
@@ -10,6 +10,9 @@ public final class ForEachLoop extends LoopType {
public
ForEachLoop
(
RegisterArg
varArg
,
InsnArg
iterableArg
)
{
this
.
varArg
=
varArg
;
this
.
iterableArg
=
iterableArg
;
// will be declared at codegen
varArg
.
getSVar
().
getCodeVar
().
setDeclared
(
true
);
}
public
RegisterArg
getVarArg
()
{
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java
View file @
9b091b7c
...
...
@@ -236,6 +236,7 @@ public class ModVisitor extends AbstractVisitor {
SSAVar
sVar
=
reg
.
getSVar
();
if
(
sVar
!=
null
)
{
sVar
.
add
(
AFlag
.
FINAL
);
sVar
.
getCodeVar
().
setFinal
(
true
);
sVar
.
add
(
AFlag
.
DONT_INLINE
);
}
reg
.
add
(
AFlag
.
SKIP_ARG
);
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java
View file @
9b091b7c
...
...
@@ -14,6 +14,7 @@ import jadx.core.dex.instructions.mods.ConstructorInsn;
import
jadx.core.dex.nodes.BlockNode
;
import
jadx.core.dex.nodes.InsnNode
;
import
jadx.core.dex.nodes.MethodNode
;
import
jadx.core.dex.visitors.regions.variables.ProcessVariables
;
import
jadx.core.utils.exceptions.JadxException
;
/**
...
...
@@ -24,7 +25,7 @@ import jadx.core.utils.exceptions.JadxException;
@JadxVisitor
(
name
=
"PrepareForCodeGen"
,
desc
=
"Prepare instructions for code generation pass"
,
runAfter
=
{
CodeShrinker
.
class
,
ClassModifier
.
class
}
runAfter
=
{
CodeShrinker
.
class
,
ClassModifier
.
class
,
ProcessVariables
.
class
}
)
public
class
PrepareForCodeGen
extends
AbstractVisitor
{
...
...
@@ -141,11 +142,11 @@ public class PrepareForCodeGen extends AbstractVisitor {
replace
=
true
;
}
else
if
(
arg
.
isRegister
())
{
RegisterArg
regArg
=
(
RegisterArg
)
arg
;
replace
=
res
.
equalRegisterAndType
(
regArg
);
replace
=
res
.
sameCodeVar
(
regArg
);
}
if
(
replace
)
{
insn
.
add
(
AFlag
.
ARITH_ONEARG
);
insn
.
getResult
().
mergeName
(
arg
);
//
insn.getResult().mergeName(arg);
}
}
}
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockExceptionHandler.java
View file @
9b091b7c
...
...
@@ -57,6 +57,7 @@ public class BlockExceptionHandler extends AbstractVisitor {
resArg
.
copyAttributesFrom
(
me
);
me
.
setResult
(
resArg
);
me
.
add
(
AFlag
.
DONT_INLINE
);
resArg
.
add
(
AFlag
.
CUSTOM_DECLARE
);
excHandler
.
setArg
(
resArg
);
return
;
}
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java
View file @
9b091b7c
...
...
@@ -34,10 +34,16 @@ import jadx.core.dex.regions.loops.LoopRegion;
import
jadx.core.dex.regions.loops.LoopType
;
import
jadx.core.dex.visitors.AbstractVisitor
;
import
jadx.core.dex.visitors.CodeShrinker
;
import
jadx.core.dex.visitors.JadxVisitor
;
import
jadx.core.dex.visitors.regions.variables.ProcessVariables
;
import
jadx.core.utils.BlockUtils
;
import
jadx.core.utils.InstructionRemover
;
import
jadx.core.utils.RegionUtils
;
@JadxVisitor
(
name
=
"LoopRegionVisitor"
,
desc
=
"Convert 'while' loops to 'for' loops (indexed or for-each)"
,
runBefore
=
{
ProcessVariables
.
class
}
)
public
class
LoopRegionVisitor
extends
AbstractVisitor
implements
IRegionVisitor
{
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
LoopRegionVisitor
.
class
);
...
...
@@ -65,9 +71,7 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
if
(
checkForIndexedLoop
(
mth
,
loopRegion
,
condition
))
{
return
;
}
if
(
checkIterableForEach
(
mth
,
loopRegion
,
condition
))
{
return
;
}
checkIterableForEach
(
mth
,
loopRegion
,
condition
);
}
/**
...
...
@@ -119,9 +123,9 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
LoopType
arrForEach
=
checkArrayForEach
(
mth
,
initInsn
,
incrInsn
,
condition
);
if
(
arrForEach
!=
null
)
{
loopRegion
.
setType
(
arrForEach
);
return
true
;
}
else
{
loopRegion
.
setType
(
new
ForLoop
(
initInsn
,
incrInsn
));
}
loopRegion
.
setType
(
new
ForLoop
(
initInsn
,
incrInsn
));
return
true
;
}
...
...
@@ -189,10 +193,15 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
// array for each loop confirmed
len
.
add
(
AFlag
.
DONT_GENERATE
);
incrInsn
.
getResult
().
add
(
AFlag
.
DONT_GENERATE
);
condArg
.
add
(
AFlag
.
DONT_GENERATE
);
bCondArg
.
add
(
AFlag
.
DONT_GENERATE
);
arrGetInsn
.
add
(
AFlag
.
DONT_GENERATE
);
InstructionRemover
.
unbindInsn
(
mth
,
len
);
// inline array variable
if
(
arrayArg
.
isRegister
())
{
((
RegisterArg
)
arrayArg
).
getSVar
().
removeUse
((
RegisterArg
)
arrGetInsn
.
getArg
(
0
));
}
CodeShrinker
.
shrinkMethod
(
mth
);
if
(
arrGetInsn
.
contains
(
AFlag
.
WRAPPED
))
{
InsnArg
wrapArg
=
BlockUtils
.
searchWrappedInsnParent
(
mth
,
arrGetInsn
);
...
...
@@ -215,18 +224,15 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
if
(
sVar
==
null
||
sVar
.
isUsedInPhi
())
{
return
false
;
}
List
<
RegisterArg
>
u
seList
=
sVar
.
getUseList
();
List
<
RegisterArg
>
itU
seList
=
sVar
.
getUseList
();
InsnNode
assignInsn
=
iteratorArg
.
getAssignInsn
();
if
(
useList
.
size
()
!=
2
||
assignInsn
==
null
||
!
checkInvoke
(
assignInsn
,
null
,
"iterator()Ljava/util/Iterator;"
,
0
))
{
if
(
itUseList
.
size
()
!=
2
||
!
checkInvoke
(
assignInsn
,
null
,
"iterator()Ljava/util/Iterator;"
,
0
))
{
return
false
;
}
InsnArg
iterableArg
=
assignInsn
.
getArg
(
0
);
InsnNode
hasNextCall
=
useList
.
get
(
0
).
getParentInsn
();
InsnNode
nextCall
=
useList
.
get
(
1
).
getParentInsn
();
if
(
hasNextCall
==
null
||
nextCall
==
null
||
!
checkInvoke
(
hasNextCall
,
"java.util.Iterator"
,
"hasNext()Z"
,
0
)
InsnNode
hasNextCall
=
itUseList
.
get
(
0
).
getParentInsn
();
InsnNode
nextCall
=
itUseList
.
get
(
1
).
getParentInsn
();
if
(!
checkInvoke
(
hasNextCall
,
"java.util.Iterator"
,
"hasNext()Z"
,
0
)
||
!
checkInvoke
(
nextCall
,
"java.util.Iterator"
,
"next()Ljava/lang/Object;"
,
0
))
{
return
false
;
}
...
...
@@ -269,6 +275,9 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
for
(
InsnNode
insnNode
:
toSkip
)
{
insnNode
.
add
(
AFlag
.
DONT_GENERATE
);
}
for
(
RegisterArg
itArg
:
itUseList
)
{
itArg
.
add
(
AFlag
.
DONT_GENERATE
);
}
loopRegion
.
setType
(
new
ForEachLoop
(
iterVar
,
iterableArg
));
return
true
;
}
...
...
@@ -314,6 +323,9 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
* Check if instruction is a interface invoke with corresponding parameters.
*/
private
static
boolean
checkInvoke
(
InsnNode
insn
,
String
declClsFullName
,
String
mthId
,
int
argsCount
)
{
if
(
insn
==
null
)
{
return
false
;
}
if
(
insn
.
getType
()
==
InsnType
.
INVOKE
)
{
InvokeNode
inv
=
(
InvokeNode
)
insn
;
MethodInfo
callMth
=
inv
.
getCallMth
();
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java
deleted
100644 → 0
View file @
7b14e322
package
jadx
.
core
.
dex
.
visitors
.
regions
;
import
java.util.ArrayList
;
import
java.util.Iterator
;
import
java.util.LinkedHashMap
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map.Entry
;
import
java.util.Set
;
import
jadx.core.dex.attributes.AFlag
;
import
jadx.core.dex.attributes.AType
;
import
jadx.core.dex.attributes.nodes.DeclareVariablesAttr
;
import
jadx.core.dex.instructions.InsnType
;
import
jadx.core.dex.instructions.args.ArgType
;
import
jadx.core.dex.instructions.args.RegisterArg
;
import
jadx.core.dex.instructions.args.VarName
;
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.MethodNode
;
import
jadx.core.dex.regions.loops.ForLoop
;
import
jadx.core.dex.regions.loops.LoopRegion
;
import
jadx.core.dex.regions.loops.LoopType
;
import
jadx.core.dex.visitors.AbstractVisitor
;
import
jadx.core.utils.RegionUtils
;
import
jadx.core.utils.exceptions.JadxException
;
public
class
ProcessVariables
extends
AbstractVisitor
{
private
static
class
Variable
{
private
final
int
regNum
;
private
final
ArgType
type
;
public
Variable
(
RegisterArg
arg
)
{
this
.
regNum
=
arg
.
getRegNum
();
this
.
type
=
arg
.
getType
();
}
@Override
public
boolean
equals
(
Object
o
)
{
if
(
this
==
o
)
{
return
true
;
}
if
(
o
==
null
||
getClass
()
!=
o
.
getClass
())
{
return
false
;
}
Variable
variable
=
(
Variable
)
o
;
return
regNum
==
variable
.
regNum
&&
type
.
equals
(
variable
.
type
);
}
@Override
public
int
hashCode
()
{
return
31
*
regNum
+
type
.
hashCode
();
}
@Override
public
String
toString
()
{
return
"r"
+
regNum
+
":"
+
type
;
}
}
private
static
class
Usage
{
private
RegisterArg
arg
;
private
VarName
varName
;
private
IRegion
argRegion
;
private
final
Set
<
IRegion
>
uses
=
new
LinkedHashSet
<>(
2
);
private
final
Set
<
IRegion
>
assigns
=
new
LinkedHashSet
<>(
2
);
public
void
setArg
(
RegisterArg
arg
)
{
this
.
arg
=
arg
;
}
public
RegisterArg
getArg
()
{
return
arg
;
}
public
VarName
getVarName
()
{
return
varName
;
}
public
void
setVarName
(
VarName
varName
)
{
this
.
varName
=
varName
;
}
public
void
setArgRegion
(
IRegion
argRegion
)
{
this
.
argRegion
=
argRegion
;
}
public
IRegion
getArgRegion
()
{
return
argRegion
;
}
public
Set
<
IRegion
>
getAssigns
()
{
return
assigns
;
}
public
Set
<
IRegion
>
getUseRegions
()
{
return
uses
;
}
@Override
public
String
toString
()
{
return
arg
+
", a:"
+
assigns
+
", u:"
+
uses
;
}
}
private
static
class
CollectUsageRegionVisitor
extends
TracedRegionVisitor
{
private
final
List
<
RegisterArg
>
args
;
private
final
Map
<
Variable
,
Usage
>
usageMap
;
public
CollectUsageRegionVisitor
(
Map
<
Variable
,
Usage
>
usageMap
)
{
this
.
usageMap
=
usageMap
;
this
.
args
=
new
ArrayList
<>();
}
@Override
public
void
processBlockTraced
(
MethodNode
mth
,
IBlock
container
,
IRegion
curRegion
)
{
regionProcess
(
curRegion
);
int
len
=
container
.
getInstructions
().
size
();
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
InsnNode
insn
=
container
.
getInstructions
().
get
(
i
);
if
(
insn
.
contains
(
AFlag
.
DONT_GENERATE
))
{
continue
;
}
args
.
clear
();
processInsn
(
insn
,
curRegion
);
}
}
private
void
regionProcess
(
IRegion
region
)
{
if
(
region
instanceof
LoopRegion
)
{
LoopRegion
loopRegion
=
(
LoopRegion
)
region
;
LoopType
loopType
=
loopRegion
.
getType
();
if
(
loopType
instanceof
ForLoop
)
{
ForLoop
forLoop
=
(
ForLoop
)
loopType
;
processInsn
(
forLoop
.
getInitInsn
(),
region
);
processInsn
(
forLoop
.
getIncrInsn
(),
region
);
}
}
}
void
processInsn
(
InsnNode
insn
,
IRegion
curRegion
)
{
if
(
insn
==
null
)
{
return
;
}
// result
RegisterArg
result
=
insn
.
getResult
();
if
(
result
!=
null
&&
result
.
isRegister
())
{
Usage
u
=
addToUsageMap
(
result
,
usageMap
);
if
(
u
.
getArg
()
==
null
)
{
u
.
setArg
(
result
);
u
.
setArgRegion
(
curRegion
);
}
u
.
getAssigns
().
add
(
curRegion
);
}
// args
args
.
clear
();
insn
.
getRegisterArgs
(
args
);
for
(
RegisterArg
arg
:
args
)
{
Usage
u
=
addToUsageMap
(
arg
,
usageMap
);
u
.
getUseRegions
().
add
(
curRegion
);
}
}
}
@Override
public
void
visit
(
MethodNode
mth
)
throws
JadxException
{
if
(
mth
.
isNoCode
())
{
return
;
}
List
<
RegisterArg
>
mthArguments
=
mth
.
getArguments
(
true
);
Map
<
Variable
,
Usage
>
usageMap
=
new
LinkedHashMap
<>();
for
(
RegisterArg
arg
:
mthArguments
)
{
addToUsageMap
(
arg
,
usageMap
);
}
// collect all variables usage
IRegionVisitor
collect
=
new
CollectUsageRegionVisitor
(
usageMap
);
DepthRegionTraversal
.
traverse
(
mth
,
collect
);
// reduce assigns map
for
(
RegisterArg
arg
:
mthArguments
)
{
usageMap
.
remove
(
new
Variable
(
arg
));
}
Iterator
<
Entry
<
Variable
,
Usage
>>
umIt
=
usageMap
.
entrySet
().
iterator
();
while
(
umIt
.
hasNext
())
{
Entry
<
Variable
,
Usage
>
entry
=
umIt
.
next
();
Usage
u
=
entry
.
getValue
();
// if no assigns => remove
if
(
u
.
getAssigns
().
isEmpty
())
{
umIt
.
remove
();
continue
;
}
// variable declared at 'catch' clause
InsnNode
parentInsn
=
u
.
getArg
().
getParentInsn
();
if
(
parentInsn
==
null
||
parentInsn
.
getType
()
==
InsnType
.
MOVE_EXCEPTION
)
{
umIt
.
remove
();
}
}
if
(
usageMap
.
isEmpty
())
{
return
;
}
for
(
Iterator
<
Entry
<
Variable
,
Usage
>>
it
=
usageMap
.
entrySet
().
iterator
();
it
.
hasNext
();
)
{
Entry
<
Variable
,
Usage
>
entry
=
it
.
next
();
Usage
u
=
entry
.
getValue
();
// check if variable can be declared at current assigns
for
(
IRegion
assignRegion
:
u
.
getAssigns
())
{
if
(
u
.
getArgRegion
()
==
assignRegion
&&
canDeclareInRegion
(
u
,
assignRegion
)
&&
declareAtAssign
(
u
))
{
it
.
remove
();
break
;
}
}
}
if
(
usageMap
.
isEmpty
())
{
return
;
}
// apply
for
(
Entry
<
Variable
,
Usage
>
entry
:
usageMap
.
entrySet
())
{
Usage
u
=
entry
.
getValue
();
// find region which contain all usage regions
Set
<
IRegion
>
set
=
u
.
getUseRegions
();
for
(
Iterator
<
IRegion
>
it
=
set
.
iterator
();
it
.
hasNext
();
)
{
IRegion
r
=
it
.
next
();
IRegion
parent
=
r
.
getParent
();
if
(
parent
!=
null
&&
set
.
contains
(
parent
))
{
it
.
remove
();
}
}
IRegion
region
=
null
;
if
(!
set
.
isEmpty
())
{
region
=
set
.
iterator
().
next
();
}
else
if
(!
u
.
getAssigns
().
isEmpty
())
{
region
=
u
.
getAssigns
().
iterator
().
next
();
}
if
(
region
==
null
)
{
continue
;
}
IRegion
parent
=
region
;
boolean
declared
=
false
;
while
(
parent
!=
null
)
{
if
(
canDeclareInRegion
(
u
,
region
))
{
declareVar
(
region
,
u
.
getArg
());
declared
=
true
;
break
;
}
region
=
parent
;
parent
=
region
.
getParent
();
}
if
(!
declared
)
{
declareVar
(
mth
.
getRegion
(),
u
.
getArg
());
}
}
}
private
static
Usage
addToUsageMap
(
RegisterArg
arg
,
Map
<
Variable
,
Usage
>
usageMap
)
{
Variable
varId
=
new
Variable
(
arg
);
Usage
usage
=
usageMap
.
computeIfAbsent
(
varId
,
v
->
new
Usage
());
// merge variables names
if
(
usage
.
getVarName
()
==
null
)
{
VarName
argVN
=
arg
.
getSVar
().
getVarName
();
if
(
argVN
==
null
)
{
argVN
=
new
VarName
();
arg
.
getSVar
().
setVarName
(
argVN
);
}
usage
.
setVarName
(
argVN
);
}
else
{
arg
.
getSVar
().
setVarName
(
usage
.
getVarName
());
}
return
usage
;
}
private
static
boolean
declareAtAssign
(
Usage
u
)
{
RegisterArg
arg
=
u
.
getArg
();
InsnNode
parentInsn
=
arg
.
getParentInsn
();
if
(
parentInsn
==
null
)
{
return
false
;
}
if
(!
arg
.
equals
(
parentInsn
.
getResult
()))
{
return
false
;
}
parentInsn
.
add
(
AFlag
.
DECLARE_VAR
);
return
true
;
}
private
static
void
declareVar
(
IContainer
region
,
RegisterArg
arg
)
{
DeclareVariablesAttr
dv
=
region
.
get
(
AType
.
DECLARE_VARIABLES
);
if
(
dv
==
null
)
{
dv
=
new
DeclareVariablesAttr
();
region
.
addAttr
(
dv
);
}
dv
.
addVar
(
arg
);
}
private
static
boolean
canDeclareInRegion
(
Usage
u
,
IRegion
region
)
{
// workaround for declare variables used in several loops
if
(
region
instanceof
LoopRegion
)
{
for
(
IRegion
r
:
u
.
getAssigns
())
{
if
(!
RegionUtils
.
isRegionContainsRegion
(
region
,
r
))
{
return
false
;
}
}
}
// can't declare in else-if chain between 'else' and next 'if'
if
(
region
.
contains
(
AFlag
.
ELSE_IF_CHAIN
))
{
return
false
;
}
// TODO: make index for faster search
return
isAllRegionsAfter
(
region
,
u
.
getAssigns
())
&&
isAllRegionsAfter
(
region
,
u
.
getUseRegions
());
}
private
static
boolean
isAllRegionsAfter
(
IRegion
region
,
Set
<
IRegion
>
others
)
{
for
(
IRegion
r
:
others
)
{
if
(!
RegionUtils
.
isRegionContainsRegion
(
region
,
r
))
{
return
false
;
}
}
return
true
;
}
}
jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java
View file @
9b091b7c
...
...
@@ -35,7 +35,7 @@ import jadx.core.utils.exceptions.JadxException;
public
class
RegionMakerVisitor
extends
AbstractVisitor
{
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
RegionMakerVisitor
.
class
);
private
static
final
Post
RegionVisitor
POST_REGION_VISITOR
=
new
PostRegionVisitor
();
private
static
final
I
RegionVisitor
POST_REGION_VISITOR
=
new
PostRegionVisitor
();
@Override
public
void
visit
(
MethodNode
mth
)
throws
JadxException
{
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/CollectUsageRegionVisitor.java
0 → 100644
View file @
9b091b7c
package
jadx
.
core
.
dex
.
visitors
.
regions
.
variables
;
import
java.util.ArrayList
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.Map
;
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.IBlock
;
import
jadx.core.dex.nodes.IRegion
;
import
jadx.core.dex.nodes.InsnNode
;
import
jadx.core.dex.nodes.MethodNode
;
import
jadx.core.dex.regions.loops.ForLoop
;
import
jadx.core.dex.regions.loops.LoopRegion
;
import
jadx.core.dex.regions.loops.LoopType
;
import
jadx.core.dex.visitors.regions.TracedRegionVisitor
;
class
CollectUsageRegionVisitor
extends
TracedRegionVisitor
{
private
final
List
<
RegisterArg
>
args
;
private
final
Map
<
SSAVar
,
VarUsage
>
usageMap
;
public
CollectUsageRegionVisitor
()
{
this
.
usageMap
=
new
LinkedHashMap
<>();
this
.
args
=
new
ArrayList
<>();
}
public
Map
<
SSAVar
,
VarUsage
>
getUsageMap
()
{
return
usageMap
;
}
@Override
public
void
processBlockTraced
(
MethodNode
mth
,
IBlock
block
,
IRegion
curRegion
)
{
UsePlace
usePlace
=
new
UsePlace
(
curRegion
,
block
);
regionProcess
(
usePlace
);
int
len
=
block
.
getInstructions
().
size
();
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
InsnNode
insn
=
block
.
getInstructions
().
get
(
i
);
if
(
insn
.
contains
(
AFlag
.
DONT_GENERATE
))
{
continue
;
}
processInsn
(
insn
,
usePlace
);
}
}
private
void
regionProcess
(
UsePlace
usePlace
)
{
IRegion
region
=
usePlace
.
getRegion
();
if
(
region
instanceof
LoopRegion
)
{
LoopRegion
loopRegion
=
(
LoopRegion
)
region
;
LoopType
loopType
=
loopRegion
.
getType
();
if
(
loopType
instanceof
ForLoop
)
{
ForLoop
forLoop
=
(
ForLoop
)
loopType
;
processInsn
(
forLoop
.
getInitInsn
(),
usePlace
);
processInsn
(
forLoop
.
getIncrInsn
(),
usePlace
);
}
}
}
void
processInsn
(
InsnNode
insn
,
UsePlace
usePlace
)
{
if
(
insn
==
null
)
{
return
;
}
// result
RegisterArg
result
=
insn
.
getResult
();
if
(
result
!=
null
&&
result
.
isRegister
())
{
if
(!
result
.
contains
(
AFlag
.
DONT_GENERATE
))
{
VarUsage
usage
=
getUsage
(
result
.
getSVar
());
usage
.
getAssigns
().
add
(
usePlace
);
}
}
// args
args
.
clear
();
insn
.
getRegisterArgs
(
args
);
for
(
RegisterArg
arg
:
args
)
{
if
(
arg
.
contains
(
AFlag
.
DONT_GENERATE
))
{
continue
;
}
VarUsage
usage
=
getUsage
(
arg
.
getSVar
());
usage
.
getUses
().
add
(
usePlace
);
}
}
private
VarUsage
getUsage
(
SSAVar
ssaVar
)
{
return
usageMap
.
computeIfAbsent
(
ssaVar
,
VarUsage:
:
new
);
}
}
jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/ProcessVariables.java
0 → 100644
View file @
9b091b7c
package
jadx
.
core
.
dex
.
visitors
.
regions
.
variables
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.HashSet
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map.Entry
;
import
java.util.Set
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
jadx.core.dex.attributes.AFlag
;
import
jadx.core.dex.attributes.AType
;
import
jadx.core.dex.attributes.nodes.DeclareVariablesAttr
;
import
jadx.core.dex.instructions.args.CodeVar
;
import
jadx.core.dex.instructions.args.RegisterArg
;
import
jadx.core.dex.instructions.args.SSAVar
;
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.MethodNode
;
import
jadx.core.dex.regions.loops.LoopRegion
;
import
jadx.core.dex.visitors.AbstractVisitor
;
import
jadx.core.dex.visitors.regions.DepthRegionTraversal
;
import
jadx.core.utils.RegionUtils
;
import
jadx.core.utils.exceptions.JadxException
;
public
class
ProcessVariables
extends
AbstractVisitor
{
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
ProcessVariables
.
class
);
@Override
public
void
visit
(
MethodNode
mth
)
throws
JadxException
{
if
(
mth
.
isNoCode
()
||
mth
.
getSVars
().
isEmpty
())
{
return
;
}
List
<
CodeVar
>
codeVars
=
collectCodeVars
(
mth
);
if
(
codeVars
.
isEmpty
())
{
return
;
}
// TODO: reduce code vars by name if debug info applied. Need checks for variable scopes before reduce
// collect all variables usage
CollectUsageRegionVisitor
usageCollector
=
new
CollectUsageRegionVisitor
();
DepthRegionTraversal
.
traverse
(
mth
,
usageCollector
);
Map
<
SSAVar
,
VarUsage
>
ssaUsageMap
=
usageCollector
.
getUsageMap
();
if
(
ssaUsageMap
.
isEmpty
())
{
return
;
}
Map
<
CodeVar
,
List
<
VarUsage
>>
codeVarUsage
=
mergeUsageMaps
(
codeVars
,
ssaUsageMap
);
for
(
Entry
<
CodeVar
,
List
<
VarUsage
>>
entry
:
codeVarUsage
.
entrySet
())
{
declareVar
(
mth
,
entry
.
getKey
(),
entry
.
getValue
());
}
}
private
void
declareVar
(
MethodNode
mth
,
CodeVar
codeVar
,
List
<
VarUsage
>
usageList
)
{
if
(
codeVar
.
isDeclared
())
{
return
;
}
VarUsage
mergedUsage
=
new
VarUsage
(
null
);
for
(
VarUsage
varUsage
:
usageList
)
{
mergedUsage
.
getAssigns
().
addAll
(
varUsage
.
getAssigns
());
mergedUsage
.
getUses
().
addAll
(
varUsage
.
getUses
());
}
if
(
mergedUsage
.
getAssigns
().
isEmpty
()
&&
mergedUsage
.
getUses
().
isEmpty
())
{
return
;
}
// check if variable can be declared at one of assigns
if
(
checkDeclareAtAssign
(
usageList
,
mergedUsage
))
{
return
;
}
// search closest region for declare
if
(
searchDeclareRegion
(
mergedUsage
,
codeVar
))
{
return
;
}
// region not found, declare at method start
declareVarInRegion
(
mth
.
getRegion
(),
codeVar
);
}
private
List
<
CodeVar
>
collectCodeVars
(
MethodNode
mth
)
{
Map
<
CodeVar
,
List
<
SSAVar
>>
codeVars
=
new
LinkedHashMap
<>();
for
(
SSAVar
ssaVar
:
mth
.
getSVars
())
{
if
(
ssaVar
.
getCodeVar
().
isThis
())
{
continue
;
}
CodeVar
codeVar
=
ssaVar
.
getCodeVar
();
List
<
SSAVar
>
list
=
codeVars
.
computeIfAbsent
(
codeVar
,
k
->
new
ArrayList
<>());
list
.
add
(
ssaVar
);
}
for
(
Entry
<
CodeVar
,
List
<
SSAVar
>>
entry
:
codeVars
.
entrySet
())
{
CodeVar
codeVar
=
entry
.
getKey
();
List
<
SSAVar
>
list
=
entry
.
getValue
();
for
(
SSAVar
ssaVar
:
list
)
{
CodeVar
localCodeVar
=
ssaVar
.
getCodeVar
();
codeVar
.
mergeFlagsFrom
(
localCodeVar
);
}
if
(
list
.
size
()
>
1
)
{
for
(
SSAVar
ssaVar
:
list
)
{
ssaVar
.
setCodeVar
(
codeVar
);
}
}
codeVar
.
setSsaVars
(
list
);
}
return
new
ArrayList
<>(
codeVars
.
keySet
());
}
private
Map
<
CodeVar
,
List
<
VarUsage
>>
mergeUsageMaps
(
List
<
CodeVar
>
codeVars
,
Map
<
SSAVar
,
VarUsage
>
ssaUsageMap
)
{
Map
<
CodeVar
,
List
<
VarUsage
>>
codeVarUsage
=
new
LinkedHashMap
<>(
codeVars
.
size
());
for
(
CodeVar
codeVar
:
codeVars
)
{
List
<
VarUsage
>
list
=
new
ArrayList
<>();
for
(
SSAVar
ssaVar
:
codeVar
.
getSsaVars
())
{
VarUsage
usage
=
ssaUsageMap
.
get
(
ssaVar
);
if
(
usage
!=
null
)
{
list
.
add
(
usage
);
}
}
if
(!
list
.
isEmpty
())
{
codeVarUsage
.
put
(
codeVar
,
list
);
}
}
return
codeVarUsage
;
}
private
boolean
checkDeclareAtAssign
(
List
<
VarUsage
>
list
,
VarUsage
mergedUsage
)
{
if
(
mergedUsage
.
getAssigns
().
isEmpty
())
{
return
false
;
}
for
(
VarUsage
u
:
list
)
{
for
(
UsePlace
assign
:
u
.
getAssigns
())
{
if
(
canDeclareAt
(
mergedUsage
,
assign
))
{
return
checkDeclareAtAssign
(
u
.
getVar
());
}
}
}
return
false
;
}
private
static
boolean
canDeclareAt
(
VarUsage
usage
,
UsePlace
usePlace
)
{
IRegion
region
=
usePlace
.
getRegion
();
// workaround for declare variables used in several loops
if
(
region
instanceof
LoopRegion
)
{
for
(
UsePlace
use
:
usage
.
getAssigns
())
{
if
(!
RegionUtils
.
isRegionContainsRegion
(
region
,
use
.
getRegion
()))
{
return
false
;
}
}
}
// can't declare in else-if chain between 'else' and next 'if'
if
(
region
.
contains
(
AFlag
.
ELSE_IF_CHAIN
))
{
return
false
;
}
return
isAllUseAfter
(
usePlace
,
usage
.
getAssigns
())
&&
isAllUseAfter
(
usePlace
,
usage
.
getUses
());
}
/**
* Check if all {@code usePlaces} are after {@code checkPlace}
*/
private
static
boolean
isAllUseAfter
(
UsePlace
checkPlace
,
List
<
UsePlace
>
usePlaces
)
{
IRegion
region
=
checkPlace
.
getRegion
();
IBlock
block
=
checkPlace
.
getBlock
();
Set
<
UsePlace
>
toCheck
=
new
HashSet
<>(
usePlaces
);
boolean
blockFound
=
false
;
for
(
IContainer
subBlock
:
region
.
getSubBlocks
())
{
if
(!
blockFound
&&
subBlock
==
block
)
{
blockFound
=
true
;
}
if
(
blockFound
)
{
toCheck
.
removeIf
(
usePlace
->
isContainerContainsUsePlace
(
subBlock
,
usePlace
));
if
(
toCheck
.
isEmpty
())
{
return
true
;
}
}
}
return
false
;
}
private
static
boolean
isContainerContainsUsePlace
(
IContainer
subBlock
,
UsePlace
usePlace
)
{
if
(
subBlock
==
usePlace
.
getBlock
())
{
return
true
;
}
if
(
subBlock
instanceof
IRegion
)
{
// TODO: make index for faster check
return
RegionUtils
.
isRegionContainsRegion
(
subBlock
,
usePlace
.
getRegion
());
}
return
false
;
}
private
static
boolean
checkDeclareAtAssign
(
SSAVar
var
)
{
RegisterArg
arg
=
var
.
getAssign
();
InsnNode
parentInsn
=
arg
.
getParentInsn
();
if
(
parentInsn
==
null
)
{
return
false
;
}
if
(!
arg
.
equals
(
parentInsn
.
getResult
()))
{
return
false
;
}
parentInsn
.
add
(
AFlag
.
DECLARE_VAR
);
return
true
;
}
private
boolean
searchDeclareRegion
(
VarUsage
u
,
CodeVar
codeVar
)
{
/*
Set<IRegion> set = u.getUseRegions();
for (Iterator<IRegion> it = set.iterator(); it.hasNext(); ) {
IRegion r = it.next();
IRegion parent = r.getParent();
if (parent != null && set.contains(parent)) {
it.remove();
}
}
IRegion region = null;
if (!set.isEmpty()) {
region = set.iterator().next();
} else if (!u.getAssigns().isEmpty()) {
region = u.getAssigns().iterator().next();
}
if (region == null) {
return false;
}
IRegion parent = region;
while (parent != null) {
if (canDeclareAt(u, region)) {
declareVarInRegion(region, codeVar);
return true;
}
region = parent;
parent = region.getParent();
}
*/
return
false
;
}
private
static
void
declareVarInRegion
(
IContainer
region
,
CodeVar
var
)
{
if
(
var
.
isDeclared
())
{
LOG
.
warn
(
"Try to declare already declared variable: {}"
,
var
);
return
;
}
DeclareVariablesAttr
dv
=
region
.
get
(
AType
.
DECLARE_VARIABLES
);
if
(
dv
==
null
)
{
dv
=
new
DeclareVariablesAttr
();
region
.
addAttr
(
dv
);
}
dv
.
addVar
(
var
);
var
.
setDeclared
(
true
);
}
private
static
boolean
isAllRegionsAfter
(
IRegion
region
,
Set
<
IRegion
>
others
)
{
IRegion
parent
=
region
.
getParent
();
if
(
parent
==
null
)
{
return
true
;
}
// lazy init for
int
regionIndex
=
-
2
;
List
<
IContainer
>
subBlocks
=
Collections
.
emptyList
();
for
(
IRegion
r
:
others
)
{
if
(
parent
==
r
.
getParent
())
{
// on same level, check order by index
if
(
regionIndex
==
-
2
)
{
subBlocks
=
parent
.
getSubBlocks
();
regionIndex
=
subBlocks
.
indexOf
(
region
);
}
int
rIndex
=
subBlocks
.
indexOf
(
r
);
if
(
regionIndex
>
rIndex
)
{
return
false
;
}
}
else
if
(!
RegionUtils
.
isRegionContainsRegion
(
region
,
r
))
{
return
false
;
}
}
return
true
;
}
}
jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/UsePlace.java
0 → 100644
View file @
9b091b7c
package
jadx
.
core
.
dex
.
visitors
.
regions
.
variables
;
import
java.util.Objects
;
import
jadx.core.dex.nodes.IBlock
;
import
jadx.core.dex.nodes.IRegion
;
public
class
UsePlace
{
public
final
IRegion
region
;
public
final
IBlock
block
;
public
UsePlace
(
IRegion
region
,
IBlock
block
)
{
this
.
region
=
region
;
this
.
block
=
block
;
}
public
IRegion
getRegion
()
{
return
region
;
}
public
IBlock
getBlock
()
{
return
block
;
}
@Override
public
boolean
equals
(
Object
o
)
{
if
(
this
==
o
)
{
return
true
;
}
if
(
o
==
null
||
getClass
()
!=
o
.
getClass
())
{
return
false
;
}
UsePlace
usePlace
=
(
UsePlace
)
o
;
return
Objects
.
equals
(
region
,
usePlace
.
region
)
&&
Objects
.
equals
(
block
,
usePlace
.
block
);
}
@Override
public
int
hashCode
()
{
return
Objects
.
hash
(
region
,
block
);
}
@Override
public
String
toString
()
{
return
"UsePlace{"
+
"region="
+
region
+
", block="
+
block
+
'}'
;
}
}
jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/VarUsage.java
0 → 100644
View file @
9b091b7c
package
jadx
.
core
.
dex
.
visitors
.
regions
.
variables
;
import
java.util.ArrayList
;
import
java.util.List
;
import
jadx.core.dex.instructions.args.SSAVar
;
class
VarUsage
{
private
final
SSAVar
var
;
private
final
List
<
UsePlace
>
assigns
=
new
ArrayList
<>(
3
);
private
final
List
<
UsePlace
>
uses
=
new
ArrayList
<>(
3
);
VarUsage
(
SSAVar
var
)
{
this
.
var
=
var
;
}
public
SSAVar
getVar
()
{
return
var
;
}
public
List
<
UsePlace
>
getAssigns
()
{
return
assigns
;
}
public
List
<
UsePlace
>
getUses
()
{
return
uses
;
}
@Override
public
String
toString
()
{
return
"{"
+
(
var
==
null
?
"-"
:
var
.
toShortString
())
+
", a:"
+
assigns
+
", u:"
+
uses
+
"}"
;
}
}
jadx-core/src/main/java/jadx/core/dex/visitors/ssa/EliminatePhiNodes.java
View file @
9b091b7c
package
jadx
.
core
.
dex
.
visitors
.
ssa
;
import
java.util.ArrayList
;
import
java.util.HashSet
;
import
java.util.Iterator
;
import
java.util.List
;
import
java.util.Set
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
jadx.core.dex.attributes.AFlag
;
import
jadx.core.dex.attributes.AType
;
import
jadx.core.dex.attributes.nodes.PhiListAttr
;
import
jadx.core.dex.instructions.InsnType
;
import
jadx.core.dex.instructions.PhiInsn
;
import
jadx.core.dex.instructions.args.CodeVar
;
import
jadx.core.dex.instructions.args.RegisterArg
;
import
jadx.core.dex.instructions.args.SSAVar
;
import
jadx.core.dex.nodes.BlockNode
;
...
...
@@ -31,6 +35,7 @@ public class EliminatePhiNodes extends AbstractVisitor {
}
replaceMergeInstructions
(
mth
);
removePhiInstructions
(
mth
);
initCodeVars
(
mth
);
}
private
static
void
removePhiInstructions
(
MethodNode
mth
)
{
...
...
@@ -56,6 +61,7 @@ public class EliminatePhiNodes extends AbstractVisitor {
}
}
LOG
.
warn
(
"Phi node not removed: {}, mth: {}"
,
phiInsn
,
mth
);
phiInsn
.
add
(
AFlag
.
DONT_GENERATE
);
}
private
void
replaceMergeInstructions
(
MethodNode
mth
)
{
...
...
@@ -128,4 +134,62 @@ public class EliminatePhiNodes extends AbstractVisitor {
phiInsn
.
bindArg
(
newArg
.
duplicate
(),
BlockUtils
.
selectOtherSafe
(
assignPred
,
block
.
getPredecessors
()));
}
private
void
initCodeVars
(
MethodNode
mth
)
{
for
(
RegisterArg
mthArg
:
mth
.
getArguments
(
true
))
{
initCodeVar
(
mthArg
.
getSVar
());
}
for
(
SSAVar
ssaVar
:
mth
.
getSVars
())
{
initCodeVar
(
ssaVar
);
}
}
private
void
initCodeVar
(
SSAVar
ssaVar
)
{
if
(
ssaVar
.
isCodeVarSet
())
{
return
;
}
CodeVar
codeVar
=
new
CodeVar
();
codeVar
.
setType
(
ssaVar
.
getTypeInfo
().
getType
());
RegisterArg
assignArg
=
ssaVar
.
getAssign
();
if
(
assignArg
.
contains
(
AFlag
.
THIS
))
{
codeVar
.
setName
(
RegisterArg
.
THIS_ARG_NAME
);
codeVar
.
setThis
(
true
);
}
if
(
assignArg
.
contains
(
AFlag
.
METHOD_ARGUMENT
)
||
assignArg
.
contains
(
AFlag
.
CUSTOM_DECLARE
))
{
codeVar
.
setDeclared
(
true
);
}
setCodeVar
(
ssaVar
,
codeVar
);
}
private
static
void
setCodeVar
(
SSAVar
ssaVar
,
CodeVar
codeVar
)
{
ssaVar
.
setCodeVar
(
codeVar
);
PhiInsn
usedInPhi
=
ssaVar
.
getUsedInPhi
();
if
(
usedInPhi
!=
null
)
{
Set
<
SSAVar
>
vars
=
new
HashSet
<>();
collectConnectedVars
(
usedInPhi
,
vars
);
vars
.
forEach
(
var
->
{
if
(
var
.
isCodeVarSet
())
{
codeVar
.
mergeFlagsFrom
(
var
.
getCodeVar
());
}
var
.
setCodeVar
(
codeVar
);
});
}
}
private
static
void
collectConnectedVars
(
PhiInsn
phiInsn
,
Set
<
SSAVar
>
vars
)
{
if
(
phiInsn
==
null
)
{
return
;
}
SSAVar
resultVar
=
phiInsn
.
getResult
().
getSVar
();
if
(
vars
.
add
(
resultVar
))
{
collectConnectedVars
(
resultVar
.
getUsedInPhi
(),
vars
);
}
phiInsn
.
getArguments
().
forEach
(
arg
->
{
SSAVar
sVar
=
((
RegisterArg
)
arg
).
getSVar
();
if
(
vars
.
add
(
sVar
))
{
collectConnectedVars
(
sVar
.
getUsedInPhi
(),
vars
);
}
});
}
}
jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java
View file @
9b091b7c
...
...
@@ -419,7 +419,6 @@ public class SSATransform extends AbstractVisitor {
return
;
}
arg
.
add
(
AFlag
.
THIS
);
arg
.
setName
(
RegisterArg
.
THIS_ARG_NAME
);
// mark all moved 'this'
InsnNode
parentInsn
=
arg
.
getParentInsn
();
if
(
parentInsn
!=
null
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/ITypeListener.java
View file @
9b091b7c
package
jadx
.
core
.
dex
.
visitors
.
typeinference
;
import
org.jetbrains.annotations.NotNull
;
import
jadx.core.dex.instructions.args.ArgType
;
import
jadx.core.dex.instructions.args.InsnArg
;
import
jadx.core.dex.nodes.InsnNode
;
...
...
@@ -14,5 +16,5 @@ public interface ITypeListener {
* @param arg apply suggested type for this arg
* @param candidateType suggest new type
*/
TypeUpdateResult
update
(
TypeUpdateInfo
updateInfo
,
InsnNode
insn
,
InsnArg
arg
,
ArgType
candidateType
);
TypeUpdateResult
update
(
TypeUpdateInfo
updateInfo
,
InsnNode
insn
,
InsnArg
arg
,
@NotNull
ArgType
candidateType
);
}
jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java
View file @
9b091b7c
...
...
@@ -58,9 +58,13 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
TypeInfo
typeInfo
=
var
.
getTypeInfo
();
ArgType
type
=
typeInfo
.
getType
();
if
(
type
!=
null
&&
!
type
.
isTypeKnown
())
{
boolean
changed
=
tryAllTypes
(
var
,
type
);
if
(!
changed
)
{
mth
.
addComment
(
"JADX WARNING: type inference failed for: "
+
var
.
getDetailedVarInfo
(
mth
));
if
(
var
.
getAssign
().
isTypeImmutable
())
{
mth
.
addComment
(
"JADX WARNING: type rejected for immutable type: "
+
var
.
getDetailedVarInfo
(
mth
));
}
else
{
boolean
changed
=
tryAllTypes
(
var
,
type
);
if
(!
changed
)
{
mth
.
addComment
(
"JADX WARNING: type inference failed for: "
+
var
.
getDetailedVarInfo
(
mth
));
}
}
}
});
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdate.java
View file @
9b091b7c
package
jadx
.
core
.
dex
.
visitors
.
typeinference
;
import
java.util.Comparator
;
import
java.util.EnumMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Objects
;
...
...
@@ -30,7 +31,7 @@ import static jadx.core.dex.visitors.typeinference.TypeUpdateResult.SAME;
public
final
class
TypeUpdate
{
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
TypeUpdate
.
class
);
private
final
TypeUpdateRegistry
listenerRegistry
;
private
final
Map
<
InsnType
,
ITypeListener
>
listenerRegistry
;
private
final
TypeCompare
comparator
;
private
ThreadLocal
<
Boolean
>
applyDebug
=
new
ThreadLocal
<>();
...
...
@@ -79,13 +80,14 @@ public final class TypeUpdate {
if
(
Objects
.
equals
(
currentType
,
candidateType
))
{
return
SAME
;
}
if
(
arg
.
isTypeImmutable
()
&&
currentType
!=
ArgType
.
UNKNOWN
)
{
return
REJECT
;
}
TypeCompareEnum
compareResult
=
comparator
.
compareTypes
(
candidateType
,
currentType
);
if
(
compareResult
==
TypeCompareEnum
.
CONFLICT
)
{
return
REJECT
;
}
if
(
arg
.
isTypeImmutable
()
&&
currentType
!=
ArgType
.
UNKNOWN
)
{
// don't changed type, conflict already rejected
return
SAME
;
}
if
(
compareResult
==
TypeCompareEnum
.
WIDER
||
compareResult
==
TypeCompareEnum
.
WIDER_BY_GENERIC
)
{
// allow wider types for apply from debug info
if
(
applyDebug
.
get
()
!=
Boolean
.
TRUE
)
{
...
...
@@ -147,21 +149,11 @@ public final class TypeUpdate {
if
(
insn
==
null
)
{
return
SAME
;
}
List
<
ITypeListener
>
listeners
=
listenerRegistry
.
getListenersForInsn
(
insn
.
getType
());
if
(
listener
s
.
isEmpty
()
)
{
ITypeListener
listener
=
listenerRegistry
.
get
(
insn
.
getType
());
if
(
listener
==
null
)
{
return
CHANGED
;
}
boolean
allSame
=
true
;
for
(
ITypeListener
listener
:
listeners
)
{
TypeUpdateResult
updateResult
=
listener
.
update
(
updateInfo
,
insn
,
arg
,
candidateType
);
if
(
updateResult
==
REJECT
)
{
return
REJECT
;
}
if
(
updateResult
!=
SAME
)
{
allSame
=
false
;
}
}
return
allSame
?
SAME
:
CHANGED
;
return
listener
.
update
(
updateInfo
,
insn
,
arg
,
candidateType
);
}
private
boolean
inBounds
(
Set
<
ITypeBound
>
bounds
,
ArgType
candidateType
)
{
...
...
@@ -227,18 +219,18 @@ public final class TypeUpdate {
return
false
;
}
private
TypeUpdateRegistry
initListenerRegistry
()
{
TypeUpdateRegistry
registry
=
new
TypeUpdateRegistry
(
);
registry
.
add
(
InsnType
.
CONST
,
this
::
sameFirstArgListener
);
registry
.
add
(
InsnType
.
MOVE
,
this
::
sameFirstArgListener
);
registry
.
add
(
InsnType
.
PHI
,
this
::
allSameListener
);
registry
.
add
(
InsnType
.
MERGE
,
this
::
allSameListener
);
registry
.
add
(
InsnType
.
AGET
,
this
::
arrayGetListener
);
registry
.
add
(
InsnType
.
APUT
,
this
::
arrayPutListener
);
registry
.
add
(
InsnType
.
IF
,
this
::
ifListener
);
registry
.
add
(
InsnType
.
ARITH
,
this
::
suggestAllSameListener
);
registry
.
add
(
InsnType
.
NEG
,
this
::
suggestAllSameListener
);
registry
.
add
(
InsnType
.
NOT
,
this
::
suggestAllSameListener
);
private
Map
<
InsnType
,
ITypeListener
>
initListenerRegistry
()
{
Map
<
InsnType
,
ITypeListener
>
registry
=
new
EnumMap
<>(
InsnType
.
class
);
registry
.
put
(
InsnType
.
CONST
,
this
::
sameFirstArgListener
);
registry
.
put
(
InsnType
.
MOVE
,
this
::
sameFirstArgListener
);
registry
.
put
(
InsnType
.
PHI
,
this
::
allSameListener
);
registry
.
put
(
InsnType
.
MERGE
,
this
::
allSameListener
);
registry
.
put
(
InsnType
.
AGET
,
this
::
arrayGetListener
);
registry
.
put
(
InsnType
.
APUT
,
this
::
arrayPutListener
);
registry
.
put
(
InsnType
.
IF
,
this
::
ifListener
);
registry
.
put
(
InsnType
.
ARITH
,
this
::
suggestAllSameListener
);
registry
.
put
(
InsnType
.
NEG
,
this
::
suggestAllSameListener
);
registry
.
put
(
InsnType
.
NOT
,
this
::
suggestAllSameListener
);
return
registry
;
}
...
...
@@ -314,7 +306,18 @@ public final class TypeUpdate {
if
(
arrayElement
==
null
)
{
return
REJECT
;
}
return
updateTypeChecked
(
updateInfo
,
putArg
,
arrayElement
);
TypeUpdateResult
result
=
updateTypeChecked
(
updateInfo
,
putArg
,
arrayElement
);
if
(
result
==
REJECT
)
{
ArgType
putType
=
putArg
.
getType
();
if
(
putType
.
isTypeKnown
()
&&
putType
.
isObject
())
{
TypeCompareEnum
compResult
=
comparator
.
compareTypes
(
arrayElement
,
putType
);
if
(
compResult
==
TypeCompareEnum
.
WIDER
||
compResult
==
TypeCompareEnum
.
WIDER_BY_GENERIC
)
{
// allow wider result (i.e allow put in Object[] any objects)
return
CHANGED
;
}
}
}
return
result
;
}
if
(
arrArg
==
putArg
)
{
return
updateTypeChecked
(
updateInfo
,
arrArg
,
ArgType
.
array
(
candidateType
));
...
...
@@ -353,6 +356,10 @@ public final class TypeUpdate {
return
insn
.
getResult
()
==
arg
;
}
public
TypeCompare
getComparator
()
{
return
comparator
;
}
public
Comparator
<
ArgType
>
getArgTypeComparator
()
{
return
comparator
.
getComparator
();
}
...
...
jadx-core/src/main/java/jadx/core/utils/DebugUtils.java
View file @
9b091b7c
...
...
@@ -4,6 +4,7 @@ import java.io.File;
import
java.util.ArrayList
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
org.jetbrains.annotations.TestOnly
;
...
...
@@ -198,4 +199,11 @@ public class DebugUtils {
}
}
}
public
static
void
printMap
(
String
desc
,
Map
<?,
?>
map
)
{
LOG
.
debug
(
"Map of {}, size: {}"
,
desc
,
map
.
size
());
for
(
Map
.
Entry
<?,
?>
entry
:
map
.
entrySet
())
{
LOG
.
debug
(
" {} : {}"
,
entry
.
getKey
(),
entry
.
getValue
());
}
}
}
jadx-core/src/main/java/jadx/core/utils/StringUtils.java
View file @
9b091b7c
...
...
@@ -203,6 +203,10 @@ public class StringUtils {
return
str
!=
null
&&
!
str
.
isEmpty
();
}
public
static
boolean
isEmpty
(
String
str
)
{
return
str
==
null
||
str
.
isEmpty
();
}
public
static
int
countMatches
(
String
str
,
String
subStr
)
{
if
(
str
==
null
||
str
.
isEmpty
()
||
subStr
==
null
||
subStr
.
isEmpty
())
{
return
0
;
...
...
jadx-core/src/test/java/jadx/core/dex/visitors/typeinference/TypeCompareTest.java
View file @
9b091b7c
...
...
@@ -57,6 +57,9 @@ public class TypeCompareTest {
public
void
compareArrays
()
{
firstIsNarrow
(
array
(
CHAR
),
OBJECT
);
firstIsNarrow
(
array
(
CHAR
),
array
(
UNKNOWN
));
firstIsNarrow
(
array
(
OBJECT
),
OBJECT
);
firstIsNarrow
(
array
(
OBJECT
),
array
(
UNKNOWN_OBJECT
));
}
@Test
...
...
jadx-core/src/test/java/jadx/tests/external/BaseExternalTest.java
View file @
9b091b7c
...
...
@@ -97,6 +97,8 @@ public abstract class BaseExternalTest extends IntegrationTest {
if
(!
decompile
)
{
return
false
;
}
// ProcessClass.process(classNode, passes, new CodeGen());
for
(
IDexTreeVisitor
visitor
:
passes
)
{
DepthTraversal
.
visit
(
visitor
,
classNode
);
}
...
...
jadx-core/src/test/java/jadx/tests/integration/TestReturnWrapping.java
View file @
9b091b7c
...
...
@@ -54,7 +54,11 @@ public class TestReturnWrapping extends IntegrationTest {
assertThat
(
code
,
containsString
(
"return 255;"
));
assertThat
(
code
,
containsString
(
"return arg0 + 1;"
));
assertThat
(
code
,
containsString
(
"return i > 128 ? arg0.toString() + ret.toString() : Integer.valueOf(i);"
));
// TODO: reduce code vars by name
// assertThat(code, containsString("return i > 128 ? arg0.toString() + ret.toString() : Integer.valueOf(i);"));
assertThat
(
code
,
containsString
(
"return i2 > 128 ? arg0.toString() + ret.toString() : Integer.valueOf(i2);"
));
assertThat
(
code
,
containsString
(
"return arg0 + 2;"
));
assertThat
(
code
,
containsString
(
"arg0 -= 951;"
));
}
...
...
jadx-core/src/test/java/jadx/tests/integration/
SimplifyVisitorStringBuilderTest
.java
→
jadx-core/src/test/java/jadx/tests/integration/
TestStringBuilderElimination2
.java
View file @
9b091b7c
package
jadx
.
tests
.
integration
;
import
org.junit.Test
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.core.dex.visitors.SimplifyVisitor
;
import
jadx.core.utils.exceptions.JadxException
;
import
jadx.tests.api.IntegrationTest
;
import
org.junit.Test
;
import
static
org
.
hamcrest
.
CoreMatchers
.
containsString
;
import
static
org
.
junit
.
Assert
.
assertThat
;
...
...
@@ -14,7 +14,7 @@ import static org.junit.Assert.assertThat;
*
* @author Jan Peter Stotz
*/
public
class
SimplifyVisitorStringBuilderTest
extends
IntegrationTest
{
public
class
TestStringBuilderElimination2
extends
IntegrationTest
{
public
static
class
TestCls1
{
public
String
test
()
{
...
...
@@ -24,10 +24,8 @@ public class SimplifyVisitorStringBuilderTest extends IntegrationTest {
}
@Test
public
void
test1
()
throws
JadxException
{
ClassNode
cls
=
getClassNode
(
SimplifyVisitorStringBuilderTest
.
TestCls1
.
class
);
SimplifyVisitor
visitor
=
new
SimplifyVisitor
();
visitor
.
visit
(
cls
);
public
void
test1
()
{
ClassNode
cls
=
getClassNode
(
TestStringBuilderElimination2
.
TestCls1
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsString
(
"return \"[init]\" + \"a1\" + 'c' + 2 + 0 + 1.0f + 2.0d + true;"
));
}
...
...
@@ -49,10 +47,8 @@ public class SimplifyVisitorStringBuilderTest extends IntegrationTest {
}
@Test
public
void
test2
()
throws
JadxException
{
ClassNode
cls
=
getClassNode
(
SimplifyVisitorStringBuilderTest
.
TestCls2
.
class
);
SimplifyVisitor
visitor
=
new
SimplifyVisitor
();
visitor
.
visit
(
cls
);
public
void
test2
()
{
ClassNode
cls
=
getClassNode
(
TestStringBuilderElimination2
.
TestCls2
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsString
(
"return \"[init]\" + \"a1\" + 'c' + 1 + 2 + 1.0f + 2.0d + true;"
));
}
...
...
@@ -68,10 +64,8 @@ public class SimplifyVisitorStringBuilderTest extends IntegrationTest {
}
@Test
public
void
test3
()
throws
JadxException
{
ClassNode
cls
=
getClassNode
(
SimplifyVisitorStringBuilderTest
.
TestClsStringUtilsReverse
.
class
);
SimplifyVisitor
visitor
=
new
SimplifyVisitor
();
visitor
.
visit
(
cls
);
public
void
test3
()
{
ClassNode
cls
=
getClassNode
(
TestClsStringUtilsReverse
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsString
(
"return new StringBuilder(str).reverse().toString();"
));
}
...
...
@@ -84,10 +78,8 @@ public class SimplifyVisitorStringBuilderTest extends IntegrationTest {
}
@Test
public
void
testChainWithDelete
()
throws
JadxException
{
public
void
testChainWithDelete
()
{
ClassNode
cls
=
getClassNode
(
TestClsChainWithDelete
.
class
);
SimplifyVisitor
visitor
=
new
SimplifyVisitor
();
visitor
.
visit
(
cls
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsString
(
"return new StringBuilder(\"[init]\").append(\"a1\").delete(1, 2).toString();"
));
}
...
...
jadx-core/src/test/java/jadx/tests/integration/arith/TestArith.java
View file @
9b091b7c
...
...
@@ -5,20 +5,23 @@ import org.junit.Test;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.tests.api.IntegrationTest
;
import
static
org
.
hamcrest
.
CoreMatchers
.
containsString
;
import
static
org
.
junit
.
Assert
.
assertThat
;
public
class
TestArith
extends
IntegrationTest
{
public
static
class
TestCls
{
public
void
method
(
int
a
)
{
public
int
test
(
int
a
)
{
a
+=
2
;
use
(
a
);
return
a
;
}
public
void
method
2
(
int
a
)
{
public
int
test
2
(
int
a
)
{
a
++;
use
(
a
);
return
a
;
}
private
static
void
use
(
int
i
)
{}
}
@Test
...
...
@@ -26,7 +29,19 @@ public class TestArith extends IntegrationTest {
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsString
(
"a += 2;"
));
assertThat
(
code
,
containsString
(
"a++;"
));
// TODO: reduce code vars by name
// assertThat(code, containsString("a += 2;"));
// assertThat(code, containsString("a++;"));
}
@Test
public
void
testNoDebug
()
{
noDebugInfo
();
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
// TODO: simplify for variables without debug names
// assertThat(code, containsString("i += 2;"));
// assertThat(code, containsString("i++;"));
}
}
jadx-core/src/test/java/jadx/tests/integration/arrays/TestArrays3.java
View file @
9b091b7c
...
...
@@ -7,6 +7,7 @@ import jadx.tests.api.IntegrationTest;
import
static
jadx
.
tests
.
api
.
utils
.
JadxMatchers
.
containsOne
;
import
static
org
.
hamcrest
.
CoreMatchers
.
instanceOf
;
import
static
org
.
hamcrest
.
Matchers
.
is
;
import
static
org
.
junit
.
Assert
.
assertThat
;
public
class
TestArrays3
extends
IntegrationTest
{
...
...
@@ -17,12 +18,23 @@ public class TestArrays3 extends IntegrationTest {
}
public
void
check
()
{
assertThat
(
test
(
new
byte
[]{
1
,
2
}),
instanceOf
(
Object
[].
class
));
byte
[]
inputArr
=
{
1
,
2
};
Object
result
=
test
(
inputArr
);
assertThat
(
result
,
instanceOf
(
Object
[].
class
));
assertThat
(((
Object
[])
result
)[
0
],
is
(
inputArr
));
}
}
@Test
public
void
test
()
{
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsOne
(
"return new Object[]{bArr};"
));
}
@Test
public
void
testNoDebug
()
{
noDebugInfo
();
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
...
...
jadx-core/src/test/java/jadx/tests/integration/generics/TestGenerics2.java
View file @
9b091b7c
...
...
@@ -4,6 +4,7 @@ import java.lang.ref.ReferenceQueue;
import
java.lang.ref.WeakReference
;
import
java.util.Map
;
import
org.junit.Ignore
;
import
org.junit.Test
;
import
jadx.core.dex.nodes.ClassNode
;
...
...
@@ -47,4 +48,14 @@ public class TestGenerics2 extends IntegrationTest {
assertThat
(
code
,
containsString
(
"WeakReference<V> ref = "
));
assertThat
(
code
,
containsString
(
"return ref.get();"
));
}
@Ignore
(
"Make generic info propagation for methods (like Map.get)"
)
@Test
public
void
testDebug
()
{
noDebugInfo
();
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsString
(
"WeakReference<V> ref = "
));
}
}
jadx-core/src/test/java/jadx/tests/integration/inline/TestInlineInLoop.java
View file @
9b091b7c
...
...
@@ -6,13 +6,12 @@ import jadx.core.dex.nodes.ClassNode;
import
jadx.tests.api.IntegrationTest
;
import
static
jadx
.
tests
.
api
.
utils
.
JadxMatchers
.
containsOne
;
import
static
jadx
.
tests
.
api
.
utils
.
JadxMatchers
.
countString
;
import
static
org
.
junit
.
Assert
.
assertThat
;
public
class
TestInlineInLoop
extends
IntegrationTest
{
public
static
class
TestCls
{
public
static
void
main
(
String
[]
args
)
throws
Exception
{
public
static
void
main
(
String
[]
args
)
{
int
a
=
0
;
int
b
=
4
;
int
c
=
0
;
...
...
@@ -37,11 +36,12 @@ public class TestInlineInLoop extends IntegrationTest {
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsOne
(
"int c"
));
assertThat
(
code
,
containsOne
(
"c = b + 1"
));
assertThat
(
code
,
countString
(
2
,
"c = b;"
));
assertThat
(
code
,
containsOne
(
"b++;"
));
assertThat
(
code
,
containsOne
(
"b = c"
));
// TODO: remove unused variables from test
assertThat
(
code
,
containsOne
(
"int c = b + 1"
));
assertThat
(
code
,
containsOne
(
"int c2 = b;"
));
assertThat
(
code
,
containsOne
(
"int c3 = b;"
));
assertThat
(
code
,
containsOne
(
"int b2 = b + 1;"
));
assertThat
(
code
,
containsOne
(
"b = c3"
));
assertThat
(
code
,
containsOne
(
"a++;"
));
}
}
jadx-core/src/test/java/jadx/tests/integration/loops/TestArrayForEach2.java
View file @
9b091b7c
...
...
@@ -6,6 +6,8 @@ import jadx.core.dex.nodes.ClassNode;
import
jadx.tests.api.IntegrationTest
;
import
static
jadx
.
tests
.
api
.
utils
.
JadxMatchers
.
containsLines
;
import
static
org
.
hamcrest
.
Matchers
.
containsString
;
import
static
org
.
hamcrest
.
Matchers
.
not
;
import
static
org
.
junit
.
Assert
.
assertThat
;
public
class
TestArrayForEach2
extends
IntegrationTest
{
...
...
@@ -26,6 +28,8 @@ public class TestArrayForEach2 extends IntegrationTest {
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
not
(
containsString
(
"int "
)));
assertThat
(
code
,
containsLines
(
2
,
"for (String s : str.split(\"\\n\")) {"
,
indent
(
1
)
+
"String t = s.trim();"
,
...
...
jadx-core/src/test/java/jadx/tests/integration/loops/TestIndexForLoop.java
View file @
9b091b7c
...
...
@@ -6,6 +6,7 @@ import jadx.core.dex.nodes.ClassNode;
import
jadx.tests.api.IntegrationTest
;
import
static
jadx
.
tests
.
api
.
utils
.
JadxMatchers
.
containsLines
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertThat
;
public
class
TestIndexForLoop
extends
IntegrationTest
{
...
...
@@ -19,6 +20,13 @@ public class TestIndexForLoop extends IntegrationTest {
}
return
sum
;
}
public
void
check
()
{
int
[]
array
=
{
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
};
assertEquals
(
0
,
test
(
array
,
0
));
assertEquals
(
6
,
test
(
array
,
3
));
assertEquals
(
36
,
test
(
array
,
8
));
}
}
@Test
...
...
jadx-core/src/test/java/jadx/tests/integration/names/TestNameAssign2.java
View file @
9b091b7c
...
...
@@ -13,7 +13,8 @@ import jadx.core.dex.nodes.MethodNode;
import
jadx.core.dex.visitors.ssa.LiveVarAnalysis
;
import
jadx.tests.api.IntegrationTest
;
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
TestNameAssign2
extends
IntegrationTest
{
...
...
@@ -58,7 +59,6 @@ public class TestNameAssign2 extends IntegrationTest {
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
// TODO:
assertThat
(
code
,
containsOne
(
"int id;"
));
assertThat
(
code
,
not
(
containsString
(
"int id;"
)));
}
}
jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase.java
View file @
9b091b7c
...
...
@@ -14,11 +14,11 @@ public class TestSwitchReturnFromCase extends IntegrationTest {
public
static
class
TestCls
{
public
void
test
(
int
a
)
{
String
s
=
null
;
if
(
a
>
1000
)
{
return
;
}
switch
(
a
%
4
)
{
String
s
=
null
;
switch
(
a
%
10
)
{
case
1
:
s
=
"1"
;
break
;
...
...
@@ -30,9 +30,14 @@ public class TestSwitchReturnFromCase extends IntegrationTest {
s
=
"4"
;
break
;
case
5
:
break
;
case
6
:
return
;
}
s
=
"5"
;
if
(
s
==
null
)
{
s
=
"5"
;
}
System
.
out
.
println
(
s
);
}
}
...
...
@@ -41,7 +46,7 @@ public class TestSwitchReturnFromCase extends IntegrationTest {
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsString
(
"switch (a %
4
) {"
));
assertThat
(
code
,
containsString
(
"switch (a %
10
) {"
));
assertEquals
(
5
,
count
(
code
,
"case "
));
assertEquals
(
3
,
count
(
code
,
"break;"
));
...
...
jadx-core/src/test/java/jadx/tests/integration/synchronize/TestSynchronized.java
View file @
9b091b7c
...
...
@@ -5,6 +5,7 @@ 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
.
hamcrest
.
CoreMatchers
.
containsString
;
import
static
org
.
hamcrest
.
CoreMatchers
.
not
;
import
static
org
.
junit
.
Assert
.
assertThat
;
...
...
@@ -22,7 +23,7 @@ public class TestSynchronized extends IntegrationTest {
public
int
test2
()
{
synchronized
(
this
.
o
)
{
return
i
;
return
this
.
i
;
}
}
}
...
...
@@ -33,9 +34,9 @@ public class TestSynchronized extends IntegrationTest {
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
not
(
containsString
(
"synchronized (this) {"
)));
assertThat
(
code
,
contains
String
(
"public synchronized boolean test1() {"
));
assertThat
(
code
,
contains
String
(
"return this.f"
));
assertThat
(
code
,
contains
String
(
"synchronized (this.o) {"
));
assertThat
(
code
,
contains
One
(
"public synchronized boolean test1() {"
));
assertThat
(
code
,
contains
One
(
"return this.f"
));
assertThat
(
code
,
contains
One
(
"synchronized (this.o) {"
));
assertThat
(
code
,
not
(
containsString
(
indent
(
3
)
+
";"
)));
assertThat
(
code
,
not
(
containsString
(
"try {"
)));
...
...
jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch3.java
View file @
9b091b7c
...
...
@@ -63,7 +63,7 @@ public class TestTryCatch3 extends IntegrationTest {
}
@Test
public
void
test
2
()
{
public
void
test
NoDebug
()
{
noDebugInfo
();
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
...
...
jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch7.java
View file @
9b091b7c
...
...
@@ -24,7 +24,7 @@ public class TestTryCatch7 extends IntegrationTest {
}
@Test
public
void
test
()
{
public
void
test
NoDebug
()
{
noDebugInfo
();
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
...
...
jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchMultiException.java
0 → 100644
View file @
9b091b7c
package
jadx
.
tests
.
integration
.
trycatch
;
import
java.security.ProviderException
;
import
java.time.DateTimeException
;
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
;
public
class
TestTryCatchMultiException
extends
IntegrationTest
{
public
static
class
TestCls
{
public
void
test
()
{
try
{
System
.
out
.
println
(
"Test"
);
}
catch
(
ProviderException
|
DateTimeException
e
)
{
throw
new
RuntimeException
(
e
);
}
}
}
@Test
public
void
test
()
{
noDebugInfo
();
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
String
catchExcVarName
=
"e"
;
assertThat
(
code
,
containsOne
(
"} catch (ProviderException | DateTimeException "
+
catchExcVarName
+
") {"
));
assertThat
(
code
,
containsOne
(
"throw new RuntimeException("
+
catchExcVarName
+
");"
));
}
}
jadx-core/src/test/java/jadx/tests/integration/types/TestArrayTypes.java
0 → 100644
View file @
9b091b7c
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
;
public
class
TestArrayTypes
extends
IntegrationTest
{
public
static
class
TestCls
{
public
void
test
()
{
Exception
e
=
new
Exception
();
System
.
out
.
println
(
e
);
use
(
new
Object
[]{
e
});
}
public
void
use
(
Object
[]
arr
)
{}
public
void
check
()
{
test
();
}
}
@Test
public
void
test
()
{
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsOne
(
"use(new Object[]{e});"
));
}
@Test
public
void
testNoDebug
()
{
noDebugInfo
();
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsOne
(
"use(new Object[]{exc});"
));
}
}
jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver5.java
View file @
9b091b7c
...
...
@@ -10,18 +10,6 @@ import static org.hamcrest.Matchers.not;
import
static
org
.
junit
.
Assert
.
assertThat
;
public
class
TestTypeResolver5
extends
SmaliTest
{
/*
Smali Code equivalent:
public static class TestCls {
public int test1(int a) {
return ~a;
}
public long test2(long b) {
return ~b;
}
}
*/
@Test
public
void
test
()
{
...
...
@@ -30,8 +18,7 @@ public class TestTypeResolver5 extends SmaliTest {
ClassNode
cls
=
getClassNodeFromSmaliWithPath
(
"types"
,
"TestTypeResolver5"
);
String
code
=
cls
.
getCode
().
toString
();
// assertThat(code, containsString("return ~a;"));
// assertThat(code, containsString("return ~b;"));
assertThat
(
code
,
not
(
containsString
(
"Object string2"
)));
assertThat
(
code
,
not
(
containsString
(
"r1v2"
)));
}
}
jadx-core/src/test/java/jadx/tests/integration/variables/TestVariables2.java
View file @
9b091b7c
...
...
@@ -28,4 +28,13 @@ public class TestVariables2 extends IntegrationTest {
assertThat
(
code
,
containsString
(
"Object store = s != null ? s : null;"
));
}
@Test
public
void
testNoDebug
()
{
noDebugInfo
();
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsString
(
"Object obj2 = obj != null ? obj : null;"
));
}
}
jadx-core/src/test/java/jadx/tests/integration/variables/TestVariablesDefinitions.java
View file @
9b091b7c
...
...
@@ -22,7 +22,7 @@ public class TestVariablesDefinitions extends IntegrationTest {
private
ClassNode
cls
;
private
List
<
IDexTreeVisitor
>
passes
;
public
void
run
()
{
public
void
test
()
{
try
{
cls
.
load
();
for
(
IDexTreeVisitor
pass
:
this
.
passes
)
{
...
...
@@ -41,5 +41,6 @@ public class TestVariablesDefinitions extends IntegrationTest {
assertThat
(
code
,
containsOne
(
indent
(
3
)
+
"for (IDexTreeVisitor pass : this.passes) {"
));
assertThat
(
code
,
not
(
containsString
(
"iterator;"
)));
assertThat
(
code
,
not
(
containsString
(
"Iterator"
)));
}
}
jadx-core/src/test/smali/types/TestTypeResolver5.smali
View file @
9b091b7c
.class public LTestTypeResolver5;
.super L
com/souq/app/activity/BaseContentActivity
;
.super L
android/content/Context
;
.source "SourceFile"
...
...
@@ -17,7 +17,7 @@
.prologue
.line 35
invoke-direct {p0}, L
com/souq/app/activity/BaseContentActivity
;-><init>()V
invoke-direct {p0}, L
android/content/Context
;-><init>()V
return-void
.end method
...
...
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