Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
Quincy379
jadx
提交
2107da2e
J
jadx
项目概览
Quincy379
/
jadx
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
J
jadx
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
2107da2e
编写于
1月 21, 2020
作者:
S
Skylot
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix: improve 'out' block detection in switch (#826)
上级
62ca30bb
变更
14
隐藏空白更改
内联
并排
Showing
14 changed file
with
345 addition
and
168 deletion
+345
-168
jadx-core/src/main/java/jadx/core/codegen/RegionGen.java
jadx-core/src/main/java/jadx/core/codegen/RegionGen.java
+25
-21
jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java
...src/main/java/jadx/core/dex/instructions/InsnDecoder.java
+1
-1
jadx-core/src/main/java/jadx/core/dex/instructions/SwitchNode.java
.../src/main/java/jadx/core/dex/instructions/SwitchNode.java
+16
-6
jadx-core/src/main/java/jadx/core/dex/regions/SwitchRegion.java
...ore/src/main/java/jadx/core/dex/regions/SwitchRegion.java
+7
-23
jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java
...src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java
+16
-0
jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java
...main/java/jadx/core/dex/visitors/regions/RegionMaker.java
+131
-110
jadx-core/src/main/java/jadx/core/utils/BlockUtils.java
jadx-core/src/main/java/jadx/core/utils/BlockUtils.java
+122
-2
jadx-core/src/main/java/jadx/core/utils/DebugUtils.java
jadx-core/src/main/java/jadx/core/utils/DebugUtils.java
+8
-0
jadx-core/src/main/java/jadx/core/utils/ImmutableList.java
jadx-core/src/main/java/jadx/core/utils/ImmutableList.java
+6
-1
jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeAssertions.java
...java/jadx/tests/api/utils/assertj/JadxCodeAssertions.java
+4
-0
jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitch2.java
...est/java/jadx/tests/integration/switches/TestSwitch2.java
+1
-1
jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchFallThrough.java
...adx/tests/integration/switches/TestSwitchFallThrough.java
+5
-3
jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase.java
.../tests/integration/switches/TestSwitchReturnFromCase.java
+2
-0
jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchWithFallThroughCase.java
...s/integration/switches/TestSwitchWithFallThroughCase.java
+1
-0
未找到文件。
jadx-core/src/main/java/jadx/core/codegen/RegionGen.java
浏览文件 @
2107da2e
...
...
@@ -275,37 +275,41 @@ public class RegionGen extends InsnGen {
List
<
Object
>
keys
=
caseInfo
.
getKeys
();
IContainer
c
=
caseInfo
.
getContainer
();
for
(
Object
k
:
keys
)
{
code
.
startLine
(
"case "
);
if
(
k
instanceof
FieldNode
)
{
FieldNode
fn
=
(
FieldNode
)
k
;
if
(
fn
.
getParentClass
().
isEnum
())
{
code
.
add
(
fn
.
getAlias
());
}
else
{
staticField
(
code
,
fn
.
getFieldInfo
());
// print original value, sometimes replace with incorrect field
FieldInitAttr
valueAttr
=
fn
.
get
(
AType
.
FIELD_INIT
);
if
(
valueAttr
!=
null
&&
valueAttr
.
getValue
()
!=
null
)
{
code
.
add
(
" /*"
).
add
(
valueAttr
.
getValue
().
toString
()).
add
(
"*/"
);
}
}
}
else
if
(
k
instanceof
Integer
)
{
code
.
add
(
TypeGen
.
literalToString
((
Integer
)
k
,
arg
.
getType
(),
mth
,
fallback
));
if
(
k
==
SwitchRegion
.
DEFAULT_CASE_KEY
)
{
code
.
startLine
(
"default:"
);
}
else
{
throw
new
JadxRuntimeException
(
"Unexpected key in switch: "
+
(
k
!=
null
?
k
.
getClass
()
:
null
));
code
.
startLine
(
"case "
);
addCaseKey
(
code
,
arg
,
k
);
code
.
add
(
':'
);
}
code
.
add
(
':'
);
}
makeRegionIndent
(
code
,
c
);
}
if
(
sw
.
getDefaultCase
()
!=
null
)
{
code
.
startLine
(
"default:"
);
makeRegionIndent
(
code
,
sw
.
getDefaultCase
());
}
code
.
decIndent
();
code
.
startLine
(
'}'
);
return
code
;
}
private
void
addCaseKey
(
CodeWriter
code
,
InsnArg
arg
,
Object
k
)
{
if
(
k
instanceof
FieldNode
)
{
FieldNode
fn
=
(
FieldNode
)
k
;
if
(
fn
.
getParentClass
().
isEnum
())
{
code
.
add
(
fn
.
getAlias
());
}
else
{
staticField
(
code
,
fn
.
getFieldInfo
());
// print original value, sometimes replace with incorrect field
FieldInitAttr
valueAttr
=
fn
.
get
(
AType
.
FIELD_INIT
);
if
(
valueAttr
!=
null
&&
valueAttr
.
getValue
()
!=
null
)
{
code
.
add
(
" /*"
).
add
(
valueAttr
.
getValue
().
toString
()).
add
(
"*/"
);
}
}
}
else
if
(
k
instanceof
Integer
)
{
code
.
add
(
TypeGen
.
literalToString
((
Integer
)
k
,
arg
.
getType
(),
mth
,
fallback
));
}
else
{
throw
new
JadxRuntimeException
(
"Unexpected key in switch: "
+
(
k
!=
null
?
k
.
getClass
()
:
null
));
}
}
private
void
makeTryCatch
(
TryCatchRegion
region
,
CodeWriter
code
)
throws
CodegenException
{
code
.
startLine
(
"try {"
);
makeRegionIndent
(
code
,
region
.
getTryRegion
());
...
...
jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java
浏览文件 @
2107da2e
...
...
@@ -623,7 +623,7 @@ public class InsnDecoder {
targets
[
i
]
=
targets
[
i
]
-
payloadOffset
+
offset
;
}
int
nextOffset
=
getNextInsnOffset
(
insnArr
,
offset
);
return
new
SwitchNode
(
InsnArg
.
reg
(
insn
,
0
,
ArgType
.
NARROW
),
keys
,
targets
,
nextOffset
);
return
new
SwitchNode
(
InsnArg
.
reg
(
insn
,
0
,
ArgType
.
NARROW
),
keys
,
targets
,
nextOffset
,
packed
);
}
private
InsnNode
fillArray
(
DecodedInstruction
insn
)
{
...
...
jadx-core/src/main/java/jadx/core/dex/instructions/SwitchNode.java
浏览文件 @
2107da2e
...
...
@@ -16,20 +16,22 @@ public class SwitchNode extends TargetInsnNode {
private
final
Object
[]
keys
;
private
final
int
[]
targets
;
private
final
int
def
;
// next instruction
private
final
boolean
packed
;
// type of switch insn, if true can contain filler keys
private
BlockNode
[]
targetBlocks
;
private
BlockNode
defTargetBlock
;
public
SwitchNode
(
InsnArg
arg
,
Object
[]
keys
,
int
[]
targets
,
int
def
)
{
this
(
keys
,
targets
,
def
);
public
SwitchNode
(
InsnArg
arg
,
Object
[]
keys
,
int
[]
targets
,
int
def
,
boolean
packed
)
{
this
(
keys
,
targets
,
def
,
packed
);
addArg
(
arg
);
}
private
SwitchNode
(
Object
[]
keys
,
int
[]
targets
,
int
def
)
{
private
SwitchNode
(
Object
[]
keys
,
int
[]
targets
,
int
def
,
boolean
packed
)
{
super
(
InsnType
.
SWITCH
,
1
);
this
.
keys
=
keys
;
this
.
targets
=
targets
;
this
.
def
=
def
;
this
.
packed
=
packed
;
}
public
int
getCasesCount
()
{
...
...
@@ -48,6 +50,10 @@ public class SwitchNode extends TargetInsnNode {
return
def
;
}
public
boolean
isPacked
()
{
return
packed
;
}
public
BlockNode
[]
getTargetBlocks
()
{
return
targetBlocks
;
}
...
...
@@ -103,7 +109,7 @@ public class SwitchNode extends TargetInsnNode {
@Override
public
InsnNode
copy
()
{
SwitchNode
copy
=
new
SwitchNode
(
keys
,
targets
,
def
);
SwitchNode
copy
=
new
SwitchNode
(
keys
,
targets
,
def
,
packed
);
copy
.
targetBlocks
=
targetBlocks
;
copy
.
defTargetBlock
=
defTargetBlock
;
return
copyCommonParams
(
copy
);
...
...
@@ -114,9 +120,13 @@ public class SwitchNode extends TargetInsnNode {
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
super
.
toString
());
for
(
int
i
=
0
;
i
<
targets
.
length
;
i
++)
{
sb
.
append
(
" case "
).
append
(
keys
[
i
])
.
append
(
": goto "
).
append
(
InsnUtils
.
formatOffset
(
targets
[
i
]));
sb
.
append
(
CodeWriter
.
NL
);
sb
.
append
(
" case "
).
append
(
keys
[
i
]);
sb
.
append
(
": goto "
).
append
(
InsnUtils
.
formatOffset
(
targets
[
i
]));
}
if
(
def
!=
-
1
)
{
sb
.
append
(
CodeWriter
.
NL
);
sb
.
append
(
" default: goto "
).
append
(
InsnUtils
.
formatOffset
(
def
));
}
return
sb
.
toString
();
}
...
...
jadx-core/src/main/java/jadx/core/dex/regions/SwitchRegion.java
浏览文件 @
2107da2e
...
...
@@ -13,10 +13,11 @@ import jadx.core.utils.Utils;
public
final
class
SwitchRegion
extends
AbstractRegion
implements
IBranchRegion
{
public
static
final
Object
DEFAULT_CASE_KEY
=
new
Object
();
private
final
BlockNode
header
;
private
final
List
<
CaseInfo
>
cases
;
private
IContainer
defCase
;
public
SwitchRegion
(
IRegion
parent
,
BlockNode
header
)
{
super
(
parent
);
...
...
@@ -50,14 +51,6 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion
cases
.
add
(
new
CaseInfo
(
keysList
,
c
));
}
public
void
setDefaultCase
(
IContainer
block
)
{
defCase
=
block
;
}
public
IContainer
getDefaultCase
()
{
return
defCase
;
}
public
List
<
CaseInfo
>
getCases
()
{
return
cases
;
}
...
...
@@ -68,23 +61,15 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion
@Override
public
List
<
IContainer
>
getSubBlocks
()
{
List
<
IContainer
>
all
=
new
ArrayList
<>(
cases
.
size
()
+
2
);
List
<
IContainer
>
all
=
new
ArrayList
<>(
cases
.
size
()
+
1
);
all
.
add
(
header
);
all
.
addAll
(
getCaseContainers
());
if
(
defCase
!=
null
)
{
all
.
add
(
defCase
);
}
return
Collections
.
unmodifiableList
(
all
);
}
@Override
public
List
<
IContainer
>
getBranches
()
{
List
<
IContainer
>
branches
=
new
ArrayList
<>(
cases
.
size
()
+
1
);
branches
.
addAll
(
getCaseContainers
());
if
(
defCase
!=
null
)
{
branches
.
add
(
defCase
);
}
return
Collections
.
unmodifiableList
(
branches
);
return
Collections
.
unmodifiableList
(
getCaseContainers
());
}
@Override
...
...
@@ -97,13 +82,12 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
"Switch: "
).
append
(
cases
.
size
());
for
(
CaseInfo
caseInfo
:
cases
)
{
List
<
String
>
keyStrings
=
Utils
.
collectionMap
(
caseInfo
.
getKeys
(),
k
->
k
==
DEFAULT_CASE_KEY
?
"default"
:
k
.
toString
());
sb
.
append
(
CodeWriter
.
NL
).
append
(
" case "
)
.
append
(
Utils
.
listToString
(
caseInfo
.
getKeys
()
))
.
append
(
Utils
.
listToString
(
keyStrings
))
.
append
(
" -> "
).
append
(
caseInfo
.
getContainer
());
}
if
(
defCase
!=
null
)
{
sb
.
append
(
CodeWriter
.
NL
).
append
(
" default -> "
).
append
(
defCase
);
}
return
sb
.
toString
();
}
}
jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java
浏览文件 @
2107da2e
...
...
@@ -27,6 +27,7 @@ public class DotGraphVisitor extends AbstractVisitor {
private
static
final
String
NL
=
"\\l"
;
private
static
final
boolean
PRINT_DOMINATORS
=
false
;
private
static
final
boolean
PRINT_DOMINATORS_INFO
=
false
;
private
final
boolean
useRegions
;
private
final
boolean
rawInsn
;
...
...
@@ -182,6 +183,14 @@ public class DotGraphVisitor extends AbstractVisitor {
if
(!
attrs
.
isEmpty
())
{
dot
.
add
(
'|'
).
add
(
attrs
);
}
if
(
PRINT_DOMINATORS_INFO
)
{
dot
.
add
(
'|'
);
dot
.
startLine
(
"doms: "
).
add
(
escape
(
block
.
getDoms
()));
dot
.
startLine
(
"\\lidom: "
).
add
(
escape
(
block
.
getIDom
()));
dot
.
startLine
(
"\\ldom-f: "
).
add
(
escape
(
block
.
getDomFrontier
()));
dot
.
startLine
(
"\\ldoms-on: "
).
add
(
escape
(
Utils
.
listToString
(
block
.
getDominatesOn
())));
dot
.
startLine
(
"\\l"
);
}
String
insns
=
insertInsns
(
mth
,
block
);
if
(!
insns
.
isEmpty
())
{
dot
.
add
(
'|'
).
add
(
insns
);
...
...
@@ -272,6 +281,13 @@ public class DotGraphVisitor extends AbstractVisitor {
}
}
private
String
escape
(
Object
obj
)
{
if
(
obj
==
null
)
{
return
"null"
;
}
return
escape
(
obj
.
toString
());
}
private
String
escape
(
String
string
)
{
return
string
.
replace
(
"\\"
,
""
)
// TODO replace \"
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java
浏览文件 @
2107da2e
...
...
@@ -2,6 +2,7 @@ package jadx.core.dex.visitors.regions;
import
java.util.ArrayList
;
import
java.util.BitSet
;
import
java.util.Collections
;
import
java.util.HashSet
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
...
...
@@ -10,6 +11,7 @@ import java.util.Map.Entry;
import
java.util.Optional
;
import
java.util.Set
;
import
org.jetbrains.annotations.Nullable
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
...
...
@@ -737,135 +739,71 @@ public class RegionMaker {
}
private
BlockNode
processSwitch
(
IRegion
currentRegion
,
BlockNode
block
,
SwitchNode
insn
,
RegionStack
stack
)
{
SwitchRegion
sw
=
new
SwitchRegion
(
currentRegion
,
block
);
currentRegion
.
getSubBlocks
().
add
(
sw
);
// map case blocks to keys
int
len
=
insn
.
getTargets
().
length
;
// sort by target
Map
<
BlockNode
,
List
<
Object
>>
blocksMap
=
new
LinkedHashMap
<>(
len
);
Object
[]
keysArr
=
insn
.
getKeys
();
BlockNode
[]
targetBlocksArr
=
insn
.
getTargetBlocks
();
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
Object
key
=
insn
.
getKeys
()[
i
];
BlockNode
targ
=
insn
.
getTargetBlocks
()[
i
];
List
<
Object
>
keys
=
blocksMap
.
computeIfAbsent
(
targ
,
k
->
new
ArrayList
<>(
2
));
keys
.
add
(
key
);
List
<
Object
>
keys
=
blocksMap
.
computeIfAbsent
(
targetBlocksArr
[
i
],
k
->
new
ArrayList
<>(
2
));
keys
.
add
(
keysArr
[
i
]);
}
BlockNode
defCase
=
insn
.
getDefTargetBlock
();
if
(
defCase
!=
null
)
{
blocksMap
.
remove
(
defCase
);
List
<
Object
>
keys
=
blocksMap
.
computeIfAbsent
(
defCase
,
k
->
new
ArrayList
<>(
1
));
keys
.
add
(
SwitchRegion
.
DEFAULT_CASE_KEY
);
}
LoopInfo
loop
=
mth
.
getLoopForBlock
(
block
);
Map
<
BlockNode
,
BlockNode
>
fallThroughCases
=
new
LinkedHashMap
<>();
List
<
BlockNode
>
basicBlocks
=
mth
.
getBasicBlocks
();
BitSet
outs
=
new
BitSet
(
basicBlocks
.
size
());
outs
.
or
(
block
.
getDomFrontier
());
for
(
BlockNode
s
:
block
.
getCleanSuccessors
())
{
BitSet
df
=
s
.
getDomFrontier
();
// fall through case block
if
(
df
.
cardinality
()
>
1
)
{
if
(
df
.
cardinality
()
>
2
)
{
LOG
.
debug
(
"Unexpected case pattern, block: {}, mth: {}"
,
s
,
mth
);
}
else
{
BlockNode
first
=
basicBlocks
.
get
(
df
.
nextSetBit
(
0
));
BlockNode
second
=
basicBlocks
.
get
(
df
.
nextSetBit
(
first
.
getId
()
+
1
));
if
(
second
.
getDomFrontier
().
get
(
first
.
getId
()))
{
fallThroughCases
.
put
(
s
,
second
);
df
=
new
BitSet
(
df
.
size
());
df
.
set
(
first
.
getId
());
}
else
if
(
first
.
getDomFrontier
().
get
(
second
.
getId
()))
{
fallThroughCases
.
put
(
s
,
first
);
df
=
new
BitSet
(
df
.
size
());
df
.
set
(
second
.
getId
());
}
}
// search 'out' block - 'next' block after whole switch statement
BlockNode
out
;
LoopInfo
loop
=
mth
.
getLoopForBlock
(
block
);
if
(
loop
==
null
)
{
out
=
calcPostDomOut
(
mth
,
block
,
mth
.
getExitBlocks
());
}
else
{
BlockNode
loopEnd
=
loop
.
getEnd
();
// treat 'continue' as exit
out
=
calcPostDomOut
(
mth
,
block
,
loopEnd
.
getPredecessors
());
if
(
out
!=
null
)
{
insertContinueInSwitch
(
block
,
out
,
loopEnd
);
}
else
{
// no 'continue'
out
=
calcPostDomOut
(
mth
,
block
,
Collections
.
singletonList
(
loopEnd
));
}
outs
.
or
(
df
);
}
outs
.
clear
(
block
.
getId
());
if
(
loop
!=
null
)
{
outs
.
clear
(
loop
.
getStart
().
getId
());
}
SwitchRegion
sw
=
new
SwitchRegion
(
currentRegion
,
block
);
currentRegion
.
getSubBlocks
().
add
(
sw
);
stack
.
push
(
sw
);
stack
.
addExits
(
BlockUtils
.
bitSetToBlocks
(
mth
,
outs
));
// check cases order if fall through case exists
if
(!
fallThroughCases
.
isEmpty
()
&&
isBadCasesOrder
(
blocksMap
,
fallThroughCases
))
{
LOG
.
debug
(
"Fixing incorrect switch cases order, method: {}"
,
mth
);
blocksMap
=
reOrderSwitchCases
(
blocksMap
,
fallThroughCases
);
if
(
isBadCasesOrder
(
blocksMap
,
fallThroughCases
))
{
mth
.
addWarn
(
"Can't fix incorrect switch cases order"
);
}
}
stack
.
addExit
(
out
);
// filter 'out' block
if
(
outs
.
cardinality
()
>
1
)
{
// remove exception handlers
BlockUtils
.
cleanBitSet
(
mth
,
outs
);
}
if
(
outs
.
cardinality
()
>
1
)
{
// filter loop start and successors of other blocks
for
(
int
i
=
outs
.
nextSetBit
(
0
);
i
>=
0
;
i
=
outs
.
nextSetBit
(
i
+
1
))
{
BlockNode
b
=
basicBlocks
.
get
(
i
);
outs
.
andNot
(
b
.
getDomFrontier
());
if
(
b
.
contains
(
AFlag
.
LOOP_START
))
{
outs
.
clear
(
b
.
getId
());
}
else
{
for
(
BlockNode
s
:
b
.
getCleanSuccessors
())
{
outs
.
clear
(
s
.
getId
());
}
// detect fallthrough cases
Map
<
BlockNode
,
BlockNode
>
fallThroughCases
=
new
LinkedHashMap
<>();
if
(
out
!=
null
)
{
BitSet
caseBlocks
=
BlockUtils
.
blocksToBitSet
(
mth
,
blocksMap
.
keySet
());
caseBlocks
.
clear
(
out
.
getId
());
for
(
BlockNode
successor
:
block
.
getCleanSuccessors
())
{
BlockNode
fallThroughBlock
=
searchFallThroughCase
(
successor
,
out
,
caseBlocks
);
if
(
fallThroughBlock
!=
null
)
{
fallThroughCases
.
put
(
successor
,
fallThroughBlock
);
}
}
}
if
(
loop
!=
null
&&
outs
.
cardinality
()
>
1
)
{
outs
.
clear
(
loop
.
getEnd
().
getId
());
}
if
(
outs
.
cardinality
()
==
0
)
{
// one or several case blocks are empty,
// run expensive algorithm for find 'out' block
for
(
BlockNode
maybeOut
:
block
.
getSuccessors
())
{
boolean
allReached
=
true
;
for
(
BlockNode
s
:
block
.
getSuccessors
())
{
if
(!
isPathExists
(
s
,
maybeOut
))
{
allReached
=
false
;
break
;
}
}
if
(
allReached
)
{
outs
.
set
(
maybeOut
.
getId
());
break
;
// check fallthrough cases order
if
(!
fallThroughCases
.
isEmpty
()
&&
isBadCasesOrder
(
blocksMap
,
fallThroughCases
))
{
Map
<
BlockNode
,
List
<
Object
>>
newBlocksMap
=
reOrderSwitchCases
(
blocksMap
,
fallThroughCases
);
if
(
isBadCasesOrder
(
newBlocksMap
,
fallThroughCases
))
{
mth
.
addComment
(
"JADX INFO: Can't fix incorrect switch cases order, some code will duplicate"
);
fallThroughCases
.
clear
();
}
else
{
blocksMap
=
newBlocksMap
;
}
}
}
BlockNode
out
=
null
;
if
(
outs
.
cardinality
()
==
1
)
{
out
=
basicBlocks
.
get
(
outs
.
nextSetBit
(
0
));
stack
.
addExit
(
out
);
}
else
if
(
loop
==
null
&&
outs
.
cardinality
()
>
1
)
{
LOG
.
warn
(
"Can't detect out node for switch block: {} in {}"
,
block
,
mth
);
}
if
(
loop
!=
null
)
{
// check if 'continue' must be inserted
BlockNode
end
=
loop
.
getEnd
();
if
(
out
!=
end
&&
out
!=
null
)
{
insertContinueInSwitch
(
block
,
out
,
end
);
}
}
if
(!
stack
.
containsExit
(
defCase
))
{
Region
defRegion
=
makeRegion
(
defCase
,
stack
);
if
(
RegionUtils
.
notEmpty
(
defRegion
))
{
sw
.
setDefaultCase
(
defRegion
);
}
}
for
(
Entry
<
BlockNode
,
List
<
Object
>>
entry
:
blocksMap
.
entrySet
())
{
List
<
Object
>
keysList
=
entry
.
getValue
();
BlockNode
caseBlock
=
entry
.
getKey
();
if
(
stack
.
containsExit
(
caseBlock
))
{
// empty case block
sw
.
addCase
(
entry
.
getValue
(),
new
Region
(
stack
.
peekRegion
()));
sw
.
addCase
(
keysList
,
new
Region
(
stack
.
peekRegion
()));
}
else
{
BlockNode
next
=
fallThroughCases
.
get
(
caseBlock
);
stack
.
addExit
(
next
);
...
...
@@ -875,17 +813,100 @@ public class RegionMaker {
next
.
add
(
AFlag
.
FALL_THROUGH
);
caseRegion
.
add
(
AFlag
.
FALL_THROUGH
);
}
sw
.
addCase
(
entry
.
getValue
()
,
caseRegion
);
sw
.
addCase
(
keysList
,
caseRegion
);
// 'break' instruction will be inserted in RegionMakerVisitor.PostRegionVisitor
}
}
removeEmptyCases
(
insn
,
sw
,
defCase
);
stack
.
pop
();
return
out
;
}
private
boolean
isBadCasesOrder
(
Map
<
BlockNode
,
List
<
Object
>>
blocksMap
,
Map
<
BlockNode
,
BlockNode
>
fallThroughCases
)
{
@Nullable
private
BlockNode
searchFallThroughCase
(
BlockNode
successor
,
BlockNode
out
,
BitSet
caseBlocks
)
{
BitSet
df
=
successor
.
getDomFrontier
();
if
(
df
.
intersects
(
caseBlocks
))
{
return
getOneIntersectionBlock
(
out
,
caseBlocks
,
df
);
}
Set
<
BlockNode
>
allPathsBlocks
=
BlockUtils
.
getAllPathsBlocks
(
successor
,
out
);
Map
<
BlockNode
,
BitSet
>
bitSetMap
=
BlockUtils
.
calcPartialPostDominance
(
mth
,
allPathsBlocks
,
out
);
BitSet
pdoms
=
bitSetMap
.
get
(
successor
);
if
(
pdoms
!=
null
&&
pdoms
.
intersects
(
caseBlocks
))
{
return
getOneIntersectionBlock
(
out
,
caseBlocks
,
pdoms
);
}
return
null
;
}
@Nullable
private
BlockNode
getOneIntersectionBlock
(
BlockNode
out
,
BitSet
caseBlocks
,
BitSet
fallThroughSet
)
{
BitSet
caseExits
=
BlockUtils
.
copyBlocksBitSet
(
mth
,
fallThroughSet
);
caseExits
.
clear
(
out
.
getId
());
caseExits
.
and
(
caseBlocks
);
return
BlockUtils
.
bitSetToOneBlock
(
mth
,
caseExits
);
}
@Nullable
private
static
BlockNode
calcPostDomOut
(
MethodNode
mth
,
BlockNode
block
,
List
<
BlockNode
>
exits
)
{
if
(
exits
.
size
()
==
1
&&
mth
.
getExitBlocks
().
equals
(
exits
))
{
// simple case: for only one exit which is equal to method exit block
return
BlockUtils
.
calcImmediatePostDominator
(
mth
,
block
);
}
// fast search: union of blocks dominance frontier
// work if no fallthrough cases and no returns inside switch
BitSet
outs
=
BlockUtils
.
copyBlocksBitSet
(
mth
,
block
.
getDomFrontier
());
for
(
BlockNode
s
:
block
.
getCleanSuccessors
())
{
outs
.
or
(
s
.
getDomFrontier
());
}
outs
.
clear
(
block
.
getId
());
if
(
outs
.
cardinality
()
!=
1
)
{
// slow search: calculate partial post-dominance for every exit node
BitSet
ipdoms
=
BlockUtils
.
newBlocksBitSet
(
mth
);
for
(
BlockNode
exitBlock
:
exits
)
{
Set
<
BlockNode
>
pathBlocks
=
BlockUtils
.
getAllPathsBlocks
(
block
,
exitBlock
);
BlockNode
ipdom
=
BlockUtils
.
calcPartialImmediatePostDominator
(
mth
,
block
,
pathBlocks
,
exitBlock
);
if
(
ipdom
!=
null
)
{
ipdoms
.
set
(
ipdom
.
getId
());
}
}
outs
.
and
(
ipdoms
);
}
return
BlockUtils
.
bitSetToOneBlock
(
mth
,
outs
);
}
/**
* Remove empty case blocks:
* 1. single 'default' case
* 2. filler cases if switch is 'packed' and 'default' case is empty
*/
private
void
removeEmptyCases
(
SwitchNode
insn
,
SwitchRegion
sw
,
BlockNode
defCase
)
{
boolean
defaultCaseIsEmpty
;
if
(
defCase
==
null
)
{
defaultCaseIsEmpty
=
true
;
}
else
{
defaultCaseIsEmpty
=
sw
.
getCases
().
stream
()
.
anyMatch
(
c
->
c
.
getKeys
().
contains
(
SwitchRegion
.
DEFAULT_CASE_KEY
)
&&
RegionUtils
.
isEmpty
(
c
.
getContainer
()));
}
if
(
defaultCaseIsEmpty
)
{
sw
.
getCases
().
removeIf
(
caseInfo
->
{
if
(
RegionUtils
.
isEmpty
(
caseInfo
.
getContainer
()))
{
List
<
Object
>
keys
=
caseInfo
.
getKeys
();
if
(
keys
.
contains
(
SwitchRegion
.
DEFAULT_CASE_KEY
))
{
return
true
;
}
if
(
insn
.
isPacked
())
{
return
true
;
}
}
return
false
;
});
}
}
private
boolean
isBadCasesOrder
(
Map
<
BlockNode
,
List
<
Object
>>
blocksMap
,
Map
<
BlockNode
,
BlockNode
>
fallThroughCases
)
{
BlockNode
nextCaseBlock
=
null
;
for
(
BlockNode
caseBlock
:
blocksMap
.
keySet
())
{
if
(
nextCaseBlock
!=
null
&&
!
caseBlock
.
equals
(
nextCaseBlock
))
{
...
...
jadx-core/src/main/java/jadx/core/utils/BlockUtils.java
浏览文件 @
2107da2e
...
...
@@ -4,9 +4,11 @@ import java.util.ArrayList;
import
java.util.BitSet
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
org.jetbrains.annotations.Nullable
;
...
...
@@ -285,7 +287,19 @@ public class BlockUtils {
return
null
;
}
public
static
BitSet
blocksToBitSet
(
MethodNode
mth
,
List
<
BlockNode
>
blocks
)
{
public
static
BitSet
newBlocksBitSet
(
MethodNode
mth
)
{
return
new
BitSet
(
mth
.
getBasicBlocks
().
size
());
}
public
static
BitSet
copyBlocksBitSet
(
MethodNode
mth
,
BitSet
bitSet
)
{
BitSet
copy
=
new
BitSet
(
mth
.
getBasicBlocks
().
size
());
if
(!
bitSet
.
isEmpty
())
{
copy
.
or
(
bitSet
);
}
return
copy
;
}
public
static
BitSet
blocksToBitSet
(
MethodNode
mth
,
Collection
<
BlockNode
>
blocks
)
{
BitSet
bs
=
new
BitSet
(
mth
.
getBasicBlocks
().
size
());
for
(
BlockNode
block
:
blocks
)
{
bs
.
set
(
block
.
getId
());
...
...
@@ -293,8 +307,16 @@ public class BlockUtils {
return
bs
;
}
@Nullable
public
static
BlockNode
bitSetToOneBlock
(
MethodNode
mth
,
BitSet
bs
)
{
if
(
bs
==
null
||
bs
.
cardinality
()
!=
1
)
{
return
null
;
}
return
mth
.
getBasicBlocks
().
get
(
bs
.
nextSetBit
(
0
));
}
public
static
List
<
BlockNode
>
bitSetToBlocks
(
MethodNode
mth
,
BitSet
bs
)
{
if
(
bs
==
null
)
{
if
(
bs
==
null
||
bs
==
EmptyBitSet
.
EMPTY
)
{
return
Collections
.
emptyList
();
}
int
size
=
bs
.
cardinality
();
...
...
@@ -649,4 +671,102 @@ public class BlockUtils {
}
return
false
;
}
public
static
Map
<
BlockNode
,
BitSet
>
calcPostDominance
(
MethodNode
mth
)
{
return
calcPartialPostDominance
(
mth
,
mth
.
getBasicBlocks
(),
mth
.
getExitBlocks
().
get
(
0
));
}
public
static
Map
<
BlockNode
,
BitSet
>
calcPartialPostDominance
(
MethodNode
mth
,
Collection
<
BlockNode
>
blockNodes
,
BlockNode
exitBlock
)
{
int
blocksCount
=
mth
.
getBasicBlocks
().
size
();
Map
<
BlockNode
,
BitSet
>
map
=
new
HashMap
<>(
blocksCount
);
BitSet
initSet
=
new
BitSet
(
blocksCount
);
for
(
BlockNode
block
:
blockNodes
)
{
initSet
.
set
(
block
.
getId
());
}
for
(
BlockNode
block
:
blockNodes
)
{
BitSet
postDoms
=
new
BitSet
(
blocksCount
);
postDoms
.
or
(
initSet
);
map
.
put
(
block
,
postDoms
);
}
BitSet
exitBitSet
=
map
.
get
(
exitBlock
);
exitBitSet
.
clear
();
exitBitSet
.
set
(
exitBlock
.
getId
());
BitSet
domSet
=
new
BitSet
(
blocksCount
);
boolean
changed
;
do
{
changed
=
false
;
for
(
BlockNode
block
:
blockNodes
)
{
if
(
block
==
exitBlock
)
{
continue
;
}
BitSet
d
=
map
.
get
(
block
);
if
(!
changed
)
{
domSet
.
clear
();
domSet
.
or
(
d
);
}
for
(
BlockNode
scc
:
block
.
getSuccessors
())
{
BitSet
scPDoms
=
map
.
get
(
scc
);
if
(
scPDoms
!=
null
)
{
d
.
and
(
scPDoms
);
}
}
d
.
set
(
block
.
getId
());
if
(!
changed
&&
!
d
.
equals
(
domSet
))
{
changed
=
true
;
map
.
put
(
block
,
d
);
}
}
}
while
(
changed
);
blockNodes
.
forEach
(
block
->
{
BitSet
postDoms
=
map
.
get
(
block
);
postDoms
.
clear
(
block
.
getId
());
if
(
postDoms
.
isEmpty
())
{
map
.
put
(
block
,
EmptyBitSet
.
EMPTY
);
}
});
return
map
;
}
@Nullable
public
static
BlockNode
calcImmediatePostDominator
(
MethodNode
mth
,
BlockNode
block
)
{
BlockNode
oneSuccessor
=
Utils
.
getOne
(
block
.
getSuccessors
());
if
(
oneSuccessor
!=
null
)
{
return
oneSuccessor
;
}
return
calcImmediatePostDominator
(
mth
,
block
,
calcPostDominance
(
mth
));
}
@Nullable
public
static
BlockNode
calcPartialImmediatePostDominator
(
MethodNode
mth
,
BlockNode
block
,
Collection
<
BlockNode
>
blockNodes
,
BlockNode
exitBlock
)
{
BlockNode
oneSuccessor
=
Utils
.
getOne
(
block
.
getSuccessors
());
if
(
oneSuccessor
!=
null
)
{
return
oneSuccessor
;
}
Map
<
BlockNode
,
BitSet
>
pDomsMap
=
calcPartialPostDominance
(
mth
,
blockNodes
,
exitBlock
);
return
calcImmediatePostDominator
(
mth
,
block
,
pDomsMap
);
}
@Nullable
public
static
BlockNode
calcImmediatePostDominator
(
MethodNode
mth
,
BlockNode
block
,
Map
<
BlockNode
,
BitSet
>
postDomsMap
)
{
BlockNode
oneSuccessor
=
Utils
.
getOne
(
block
.
getSuccessors
());
if
(
oneSuccessor
!=
null
)
{
return
oneSuccessor
;
}
List
<
BlockNode
>
basicBlocks
=
mth
.
getBasicBlocks
();
BitSet
postDoms
=
postDomsMap
.
get
(
block
);
BitSet
bs
=
copyBlocksBitSet
(
mth
,
postDoms
);
for
(
int
i
=
bs
.
nextSetBit
(
0
);
i
>=
0
;
i
=
bs
.
nextSetBit
(
i
+
1
))
{
BlockNode
pdomBlock
=
basicBlocks
.
get
(
i
);
BitSet
pdoms
=
postDomsMap
.
get
(
pdomBlock
);
if
(
pdoms
!=
null
)
{
bs
.
andNot
(
pdoms
);
}
}
return
bitSetToOneBlock
(
mth
,
bs
);
}
}
jadx-core/src/main/java/jadx/core/utils/DebugUtils.java
浏览文件 @
2107da2e
...
...
@@ -5,6 +5,7 @@ import java.util.Arrays;
import
java.util.Iterator
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.stream.Collectors
;
...
...
@@ -159,4 +160,11 @@ public class DebugUtils {
cw
.
startLine
(
indent
).
add
(
"|+ "
).
add
(
it
.
next
());
}
}
public
static
void
printMap
(
Map
<?,
?>
map
,
String
desc
)
{
LOG
.
debug
(
"Map {} (size = {}):"
,
desc
,
map
.
size
());
for
(
Map
.
Entry
<?,
?>
entry
:
map
.
entrySet
())
{
LOG
.
debug
(
" {}: {}"
,
entry
.
getKey
(),
entry
.
getValue
());
}
}
}
jadx-core/src/main/java/jadx/core/utils/ImmutableList.java
浏览文件 @
2107da2e
...
...
@@ -76,7 +76,12 @@ public final class ImmutableList<E> implements List<E>, RandomAccess {
@Override
public
boolean
containsAll
(
@NotNull
Collection
<?>
c
)
{
throw
new
UnsupportedOperationException
();
for
(
Object
obj
:
c
)
{
if
(!
contains
(
obj
))
{
return
false
;
}
}
return
true
;
}
@NotNull
...
...
jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeAssertions.java
浏览文件 @
2107da2e
...
...
@@ -10,6 +10,10 @@ public class JadxCodeAssertions extends AbstractStringAssert<JadxCodeAssertions>
super
(
code
,
JadxCodeAssertions
.
class
);
}
public
JadxCodeAssertions
containsOne
(
String
substring
)
{
return
countString
(
1
,
substring
);
}
public
JadxCodeAssertions
countString
(
int
count
,
String
substring
)
{
isNotNull
();
int
actualCount
=
TestUtils
.
count
(
actual
,
substring
);
...
...
jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitch2.java
浏览文件 @
2107da2e
...
...
@@ -17,7 +17,7 @@ public class TestSwitch2 extends IntegrationTest {
boolean
isScrolling
;
float
multiTouchZoomOldDist
;
void
test
(
int
action
)
{
public
void
test
(
int
action
)
{
switch
(
action
&
255
)
{
case
0
:
this
.
isLongtouchable
=
true
;
...
...
jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchFallThrough.java
浏览文件 @
2107da2e
...
...
@@ -2,7 +2,6 @@ package jadx.tests.integration.switches;
import
org.junit.jupiter.api.Test
;
import
jadx.NotYetImplemented
;
import
jadx.tests.api.IntegrationTest
;
import
static
jadx
.
tests
.
api
.
utils
.
assertj
.
JadxAssertions
.
assertThat
;
...
...
@@ -12,6 +11,7 @@ public class TestSwitchFallThrough extends IntegrationTest {
public
static
class
TestCls
{
public
int
r
;
@SuppressWarnings
(
"fallthrough"
)
public
void
test
(
int
a
)
{
int
i
=
10
;
switch
(
a
)
{
...
...
@@ -43,12 +43,14 @@ public class TestSwitchFallThrough extends IntegrationTest {
}
}
@NotYetImplemented
(
"switch fallthrough"
)
@Test
public
void
test
()
{
assertThat
(
getClassNode
(
TestCls
.
class
))
.
code
()
.
containsOnlyOnce
(
"switch"
);
.
containsOne
(
"switch (a) {"
)
.
containsOne
(
"r = i;"
)
.
containsOne
(
"r = -1;"
)
.
countString
(
2
,
"break;"
);
// code correctness checks done in 'check' method
}
}
jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase.java
浏览文件 @
2107da2e
...
...
@@ -47,6 +47,8 @@ public class TestSwitchReturnFromCase extends IntegrationTest {
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsString
(
"switch (a % 10) {"
));
// case 5: removed
assertEquals
(
5
,
count
(
code
,
"case "
));
assertEquals
(
3
,
count
(
code
,
"break;"
));
...
...
jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchWithFallThroughCase.java
浏览文件 @
2107da2e
...
...
@@ -26,6 +26,7 @@ public class TestSwitchWithFallThroughCase extends IntegrationTest {
}
break
;
}
// fallthrough
case
2
:
if
(
b
)
{
str
+=
"2"
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录