Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
Quincy379
jadx
提交
62ca30bb
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 搜索 >>
提交
62ca30bb
编写于
1月 19, 2020
作者:
S
Skylot
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix: additional patterns to restore enum classes (#830)
上级
5e7388f6
变更
9
隐藏空白更改
内联
并排
Showing
9 changed file
with
576 addition
and
103 deletion
+576
-103
jadx-core/src/main/java/jadx/core/dex/attributes/nodes/EnumClassAttr.java
...in/java/jadx/core/dex/attributes/nodes/EnumClassAttr.java
+4
-4
jadx-core/src/main/java/jadx/core/dex/visitors/EnumVisitor.java
...ore/src/main/java/jadx/core/dex/visitors/EnumVisitor.java
+210
-97
jadx-core/src/test/java/jadx/tests/api/SmaliTest.java
jadx-core/src/test/java/jadx/tests/api/SmaliTest.java
+5
-0
jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeAssertions.java
...java/jadx/tests/api/utils/assertj/JadxCodeAssertions.java
+7
-2
jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums5.java
...rc/test/java/jadx/tests/integration/enums/TestEnums5.java
+23
-0
jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsInterface.java
...java/jadx/tests/integration/enums/TestEnumsInterface.java
+2
-0
jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsWithStaticFields.java
...dx/tests/integration/enums/TestEnumsWithStaticFields.java
+22
-0
jadx-core/src/test/smali/enums/TestEnums5.smali
jadx-core/src/test/smali/enums/TestEnums5.smali
+129
-0
jadx-core/src/test/smali/enums/TestEnumsWithStaticFields.smali
...core/src/test/smali/enums/TestEnumsWithStaticFields.smali
+174
-0
未找到文件。
jadx-core/src/main/java/jadx/core/dex/attributes/nodes/EnumClassAttr.java
浏览文件 @
62ca30bb
...
...
@@ -5,26 +5,26 @@ import java.util.List;
import
jadx.core.dex.attributes.AType
;
import
jadx.core.dex.attributes.IAttribute
;
import
jadx.core.dex.info.FieldInfo
;
import
jadx.core.dex.instructions.mods.ConstructorInsn
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.core.dex.nodes.FieldNode
;
import
jadx.core.dex.nodes.MethodNode
;
public
class
EnumClassAttr
implements
IAttribute
{
public
static
class
EnumField
{
private
final
Field
Info
field
;
private
final
Field
Node
field
;
private
final
ConstructorInsn
constrInsn
;
private
final
int
startArg
;
private
ClassNode
cls
;
public
EnumField
(
Field
Info
field
,
ConstructorInsn
co
,
int
startArg
)
{
public
EnumField
(
Field
Node
field
,
ConstructorInsn
co
,
int
startArg
)
{
this
.
field
=
field
;
this
.
constrInsn
=
co
;
this
.
startArg
=
startArg
;
}
public
Field
Info
getField
()
{
public
Field
Node
getField
()
{
return
field
;
}
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/EnumVisitor.java
浏览文件 @
62ca30bb
package
jadx.core.dex.visitors
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Objects
;
import
java.util.Optional
;
import
java.util.stream.Collectors
;
import
org.jetbrains.annotations.Nullable
;
...
...
@@ -22,7 +26,9 @@ import jadx.core.dex.instructions.InsnType;
import
jadx.core.dex.instructions.args.ArgType
;
import
jadx.core.dex.instructions.args.InsnArg
;
import
jadx.core.dex.instructions.args.InsnWrapArg
;
import
jadx.core.dex.instructions.args.LiteralArg
;
import
jadx.core.dex.instructions.args.RegisterArg
;
import
jadx.core.dex.instructions.args.SSAVar
;
import
jadx.core.dex.instructions.mods.ConstructorInsn
;
import
jadx.core.dex.nodes.BlockNode
;
import
jadx.core.dex.nodes.ClassNode
;
...
...
@@ -31,7 +37,7 @@ import jadx.core.dex.nodes.FieldNode;
import
jadx.core.dex.nodes.InsnNode
;
import
jadx.core.dex.nodes.MethodNode
;
import
jadx.core.dex.visitors.shrink.CodeShrinkVisitor
;
import
jadx.core.utils.
ErrorsCount
er
;
import
jadx.core.utils.
InsnRemov
er
;
import
jadx.core.utils.InsnUtils
;
import
jadx.core.utils.exceptions.JadxException
;
...
...
@@ -48,7 +54,7 @@ public class EnumVisitor extends AbstractVisitor {
AccessInfo
accessFlags
=
cls
.
getAccessFlags
();
if
(
accessFlags
.
isEnum
())
{
cls
.
setAccessFlags
(
accessFlags
.
remove
(
AccessFlags
.
ACC_ENUM
));
cls
.
addAttr
(
AType
.
COMMENTS
,
"'enum' modifier removed"
);
cls
.
addAttr
(
AType
.
COMMENTS
,
"
JADX INFO: Failed to restore enum class,
'enum' modifier removed"
);
}
}
return
true
;
...
...
@@ -58,122 +64,237 @@ public class EnumVisitor extends AbstractVisitor {
if
(!
cls
.
isEnum
())
{
return
false
;
}
// search class init method
MethodNode
staticMethod
=
null
;
for
(
MethodNode
mth
:
cls
.
getMethods
())
{
MethodInfo
mi
=
mth
.
getMethodInfo
();
if
(
mi
.
isClassInit
())
{
staticMethod
=
mth
;
break
;
}
MethodNode
classInitMth
=
cls
.
getClassInitMth
();
if
(
classInitMth
==
null
)
{
cls
.
addAttr
(
AType
.
COMMENTS
,
"JADX INFO: Enum class init method not found"
);
return
false
;
}
if
(
staticMethod
==
null
)
{
ErrorsCounter
.
classWarn
(
cls
,
"Enum class init method not found"
);
if
(
classInitMth
.
getBasicBlocks
().
isEmpty
())
{
return
false
;
}
BlockNode
staticBlock
=
classInitMth
.
getBasicBlocks
().
get
(
0
);
ArgType
clsType
=
cls
.
getClassInfo
().
getType
();
String
enumConstructor
=
"<init>(Ljava/lang/String;I)V"
;
// TODO: detect these methods by analyzing method instructions
String
valuesOfMethod
=
"valueOf(Ljava/lang/String;)"
+
TypeGen
.
signature
(
clsType
);
String
valuesMethod
=
"values()"
+
TypeGen
.
signature
(
ArgType
.
array
(
clsType
));
// collect enum fields, remove synthetic
List
<
FieldNode
>
enumFields
=
new
ArrayList
<>();
for
(
FieldNode
f
:
cls
.
getFields
())
{
if
(
f
.
getAccessFlags
().
isEnum
())
{
enumFields
.
add
(
f
);
f
.
add
(
AFlag
.
DONT_GENERATE
);
}
else
if
(
f
.
getAccessFlags
().
isSynthetic
())
{
f
.
add
(
AFlag
.
DONT_GENERATE
);
// search "$VALUES" field (holds all enum values)
List
<
FieldNode
>
valuesCandidates
=
cls
.
getFields
().
stream
()
.
filter
(
f
->
f
.
getAccessFlags
().
isStatic
())
.
filter
(
f
->
f
.
getType
().
isArray
())
.
filter
(
f
->
Objects
.
equals
(
f
.
getType
().
getArrayRootElement
(),
clsType
))
.
collect
(
Collectors
.
toList
());
if
(
valuesCandidates
.
isEmpty
())
{
return
false
;
}
if
(
valuesCandidates
.
size
()
>
1
)
{
valuesCandidates
.
removeIf
(
f
->
!
f
.
getAccessFlags
().
isSynthetic
());
}
if
(
valuesCandidates
.
size
()
>
1
)
{
Optional
<
FieldNode
>
valuesOpt
=
valuesCandidates
.
stream
().
filter
(
f
->
f
.
getName
().
equals
(
"$VALUES"
)).
findAny
();
if
(
valuesOpt
.
isPresent
())
{
valuesCandidates
.
clear
();
valuesCandidates
.
add
(
valuesOpt
.
get
());
}
}
if
(
valuesCandidates
.
size
()
!=
1
)
{
cls
.
addAttr
(
AType
.
COMMENTS
,
"JADX INFO: found several \"values\" enum fields: "
+
valuesCandidates
);
return
false
;
}
FieldNode
valuesField
=
valuesCandidates
.
get
(
0
);
List
<
InsnNode
>
toRemove
=
new
ArrayList
<>();
//
remove synthetic metho
ds
for
(
MethodNode
mth
:
cls
.
getMethods
())
{
MethodInfo
mi
=
mth
.
getMethodInfo
();
if
(
mi
.
isClassInit
()
)
{
//
search "$VALUES" array init and collect enum fiel
ds
List
<
EnumField
>
enumFields
=
null
;
for
(
InsnNode
insn
:
staticBlock
.
getInstructions
())
{
if
(
insn
.
getType
()
!=
InsnType
.
SPUT
)
{
continue
;
}
String
shortId
=
mi
.
getShortId
();
boolean
isSynthetic
=
mth
.
getAccessFlags
().
isSynthetic
();
if
(
mi
.
isConstructor
()
&&
!
isSynthetic
)
{
if
(
shortId
.
equals
(
enumConstructor
))
{
mth
.
add
(
AFlag
.
DONT_GENERATE
);
FieldInfo
f
=
(
FieldInfo
)
((
IndexInsnNode
)
insn
).
getIndex
();
if
(
f
.
equals
(
valuesField
.
getFieldInfo
()))
{
InsnArg
arrArg
=
insn
.
getArg
(
0
);
if
(
arrArg
.
isInsnWrap
())
{
InsnNode
arrFillInsn
=
((
InsnWrapArg
)
arrArg
).
getWrapInsn
();
InsnType
insnType
=
arrFillInsn
.
getType
();
if
(
insnType
==
InsnType
.
FILLED_NEW_ARRAY
)
{
enumFields
=
extractEnumFields
(
cls
,
arrFillInsn
,
staticBlock
,
toRemove
);
}
else
if
(
insnType
==
InsnType
.
NEW_ARRAY
)
{
// empty enum
InsnArg
arg
=
arrFillInsn
.
getArg
(
0
);
if
(
arg
.
isLiteral
()
&&
((
LiteralArg
)
arg
).
getLiteral
()
==
0
)
{
enumFields
=
Collections
.
emptyList
();
}
}
}
}
else
if
(
isSynthetic
||
shortId
.
equals
(
valuesMethod
)
||
shortId
.
equals
(
valuesOfMethod
))
{
mth
.
add
(
AFlag
.
DONT_GENERATE
);
toRemove
.
add
(
insn
);
break
;
}
}
if
(
enumFields
==
null
)
{
return
false
;
}
// all checks complete, perform transform
EnumClassAttr
attr
=
new
EnumClassAttr
(
enumFields
.
size
());
attr
.
setStaticMethod
(
classInitMth
);
attr
.
getFields
().
addAll
(
enumFields
);
cls
.
addAttr
(
attr
);
attr
.
setStaticMethod
(
staticMethod
);
ClassInfo
classInfo
=
cls
.
getClassInfo
();
for
(
EnumField
field
:
attr
.
getFields
())
{
ConstructorInsn
co
=
field
.
getConstrInsn
();
FieldNode
fieldNode
=
field
.
getField
();
// move enum specific instruction from static method to separate list
BlockNode
staticBlock
=
staticMethod
.
getBasicBlocks
().
get
(
0
);
List
<
InsnNode
>
enumPutInsns
=
new
ArrayList
<>();
List
<
InsnNode
>
list
=
staticBlock
.
getInstructions
();
int
size
=
list
.
size
();
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
InsnNode
insn
=
list
.
get
(
i
);
if
(
insn
.
getType
()
!=
InsnType
.
SPUT
)
{
continue
;
}
FieldInfo
f
=
(
FieldInfo
)
((
IndexInsnNode
)
insn
).
getIndex
();
if
(!
f
.
getDeclClass
().
equals
(
classInfo
))
{
continue
;
// use string arg from the constructor as enum field name
String
name
=
getConstString
(
cls
.
dex
(),
co
.
getArg
(
0
));
if
(
name
!=
null
&&
!
fieldNode
.
getAlias
().
equals
(
name
)
&&
NameMapper
.
isValidAndPrintable
(
name
)
&&
cls
.
root
().
getArgs
().
isRenameValid
())
{
fieldNode
.
getFieldInfo
().
setAlias
(
name
);
}
FieldNode
fieldNode
=
cls
.
searchField
(
f
);
if
(
fieldNode
!=
null
&&
isEnumArrayField
(
classInfo
,
fieldNode
))
{
if
(
i
==
size
-
1
)
{
staticMethod
.
add
(
AFlag
.
DONT_GENERATE
);
}
else
{
list
.
subList
(
0
,
i
+
1
).
clear
();
if
(!
co
.
getClassType
().
equals
(
cls
.
getClassInfo
()))
{
// enum contains additional methods
for
(
ClassNode
innerCls
:
cls
.
getInnerClasses
())
{
processEnumInnerCls
(
co
,
field
,
innerCls
);
}
break
;
}
else
{
enumPutInsns
.
add
(
insn
);
}
}
for
(
InsnNode
putInsn
:
enumPutInsns
)
{
ConstructorInsn
co
=
getConstructorInsn
(
putInsn
);
if
(
co
==
null
||
co
.
getArgsCount
()
<
2
)
{
continue
;
}
ClassInfo
clsInfo
=
co
.
getClassType
();
ClassNode
constrCls
=
cls
.
dex
().
resolveClass
(
clsInfo
);
if
(
constrCls
==
null
)
{
continue
;
valuesField
.
add
(
AFlag
.
DONT_GENERATE
);
enumFields
.
forEach
(
f
->
f
.
getField
().
add
(
AFlag
.
DONT_GENERATE
));
InsnRemover
.
removeAllAndUnbind
(
classInitMth
,
staticBlock
,
toRemove
);
if
(
classInitMth
.
countInsns
()
==
0
)
{
classInitMth
.
add
(
AFlag
.
DONT_GENERATE
);
}
removeEnumMethods
(
cls
,
clsType
);
return
true
;
}
private
List
<
EnumField
>
extractEnumFields
(
ClassNode
cls
,
InsnNode
arrFillInsn
,
BlockNode
staticBlock
,
List
<
InsnNode
>
toRemove
)
{
List
<
EnumField
>
enumFields
=
new
ArrayList
<>();
for
(
InsnArg
arg
:
arrFillInsn
.
getArguments
())
{
EnumField
field
=
null
;
if
(
arg
.
isInsnWrap
())
{
InsnNode
wrappedInsn
=
((
InsnWrapArg
)
arg
).
getWrapInsn
();
field
=
processEnumFieldByField
(
cls
,
wrappedInsn
,
staticBlock
,
toRemove
);
}
else
if
(
arg
.
isRegister
())
{
field
=
processEnumFiledByRegister
(
cls
,
((
RegisterArg
)
arg
),
toRemove
);
}
if
(
!
clsInfo
.
equals
(
classInfo
)
&&
!
constrCls
.
getAccessFlags
().
isEnum
()
)
{
continue
;
if
(
field
==
null
)
{
return
null
;
}
FieldInfo
fieldInfo
=
(
FieldInfo
)
((
IndexInsnNode
)
putInsn
).
getIndex
();
String
name
=
getConstString
(
cls
.
dex
(),
co
.
getArg
(
0
));
if
(
name
!=
null
&&
!
fieldInfo
.
getAlias
().
equals
(
name
)
&&
NameMapper
.
isValidAndPrintable
(
name
)
&&
cls
.
root
().
getArgs
().
isRenameValid
())
{
fieldInfo
.
setAlias
(
name
);
enumFields
.
add
(
field
);
}
return
enumFields
;
}
@Nullable
private
EnumField
processEnumFieldByField
(
ClassNode
cls
,
InsnNode
sgetInsn
,
BlockNode
staticBlock
,
List
<
InsnNode
>
toRemove
)
{
if
(
sgetInsn
.
getType
()
!=
InsnType
.
SGET
)
{
return
null
;
}
FieldInfo
fieldInfo
=
(
FieldInfo
)
((
IndexInsnNode
)
sgetInsn
).
getIndex
();
FieldNode
enumFieldNode
=
cls
.
searchField
(
fieldInfo
);
if
(
enumFieldNode
==
null
)
{
return
null
;
}
InsnNode
sputInsn
=
searchFieldPutInsn
(
cls
,
staticBlock
,
enumFieldNode
);
if
(
sputInsn
==
null
)
{
return
null
;
}
ConstructorInsn
co
=
getConstructorInsn
(
sputInsn
);
if
(
co
==
null
)
{
return
null
;
}
toRemove
.
add
(
sgetInsn
);
toRemove
.
add
(
sputInsn
);
toRemove
.
add
(
co
);
return
createEnumFieldByConstructor
(
cls
,
enumFieldNode
,
co
);
}
@Nullable
private
EnumField
processEnumFiledByRegister
(
ClassNode
cls
,
RegisterArg
arg
,
List
<
InsnNode
>
toRemove
)
{
SSAVar
ssaVar
=
arg
.
getSVar
();
if
(
ssaVar
.
getUseCount
()
==
1
)
{
return
null
;
}
final
InsnNode
sputInsn
=
ssaVar
.
getUseList
().
get
(
0
).
getParentInsn
();
if
(
sputInsn
==
null
||
sputInsn
.
getType
()
!=
InsnType
.
SPUT
)
{
return
null
;
}
FieldInfo
fieldInfo
=
(
FieldInfo
)
((
IndexInsnNode
)
sputInsn
).
getIndex
();
FieldNode
enumFieldNode
=
cls
.
searchField
(
fieldInfo
);
if
(
enumFieldNode
==
null
)
{
return
null
;
}
InsnNode
constrInsn
=
ssaVar
.
getAssign
().
getParentInsn
();
if
(
constrInsn
==
null
||
constrInsn
.
getType
()
!=
InsnType
.
CONSTRUCTOR
)
{
return
null
;
}
toRemove
.
add
(
sputInsn
);
toRemove
.
add
(
constrInsn
);
return
createEnumFieldByConstructor
(
cls
,
enumFieldNode
,
(
ConstructorInsn
)
constrInsn
);
}
private
EnumField
createEnumFieldByConstructor
(
ClassNode
cls
,
FieldNode
enumFieldNode
,
ConstructorInsn
co
)
{
// usually constructor signature is '<init>(Ljava/lang/String;I)V'.
// sometimes for one field enum second arg can be omitted
if
(
co
.
getArgsCount
()
<
1
)
{
return
null
;
}
ClassInfo
clsInfo
=
co
.
getClassType
();
ClassNode
constrCls
=
cls
.
dex
().
resolveClass
(
clsInfo
);
if
(
constrCls
==
null
)
{
return
null
;
}
if
(!
clsInfo
.
equals
(
cls
.
getClassInfo
())
&&
!
constrCls
.
getAccessFlags
().
isEnum
())
{
return
null
;
}
int
startArg
=
co
.
getArgsCount
()
==
1
?
1
:
2
;
return
new
EnumField
(
enumFieldNode
,
co
,
startArg
);
}
@Nullable
private
InsnNode
searchFieldPutInsn
(
ClassNode
cls
,
BlockNode
staticBlock
,
FieldNode
enumFieldNode
)
{
for
(
InsnNode
sputInsn
:
staticBlock
.
getInstructions
())
{
if
(
sputInsn
!=
null
&&
sputInsn
.
getType
()
==
InsnType
.
SPUT
)
{
FieldInfo
f
=
(
FieldInfo
)
((
IndexInsnNode
)
sputInsn
).
getIndex
();
FieldNode
fieldNode
=
cls
.
searchField
(
f
);
if
(
Objects
.
equals
(
fieldNode
,
enumFieldNode
))
{
return
sputInsn
;
}
}
}
return
null
;
}
EnumField
field
=
new
EnumField
(
fieldInfo
,
co
,
2
);
attr
.
getFields
().
add
(
field
);
// TODO: detect these methods by analyzing method instructions
private
void
removeEnumMethods
(
ClassNode
cls
,
ArgType
clsType
)
{
String
enumConstructor
=
"<init>(Ljava/lang/String;I)V"
;
String
enumConstructorAlt
=
"<init>(Ljava/lang/String;)V"
;
String
valuesOfMethod
=
"valueOf(Ljava/lang/String;)"
+
TypeGen
.
signature
(
clsType
);
String
valuesMethod
=
"values()"
+
TypeGen
.
signature
(
ArgType
.
array
(
clsType
));
if
(!
co
.
getClassType
().
equals
(
classInfo
))
{
// enum contains additional methods
for
(
ClassNode
innerCls
:
cls
.
getInnerClasses
())
{
processEnumInnerCls
(
co
,
field
,
innerCls
);
// remove synthetic methods
for
(
MethodNode
mth
:
cls
.
getMethods
())
{
MethodInfo
mi
=
mth
.
getMethodInfo
();
if
(
mi
.
isClassInit
())
{
continue
;
}
String
shortId
=
mi
.
getShortId
();
boolean
isSynthetic
=
mth
.
getAccessFlags
().
isSynthetic
();
if
(
mi
.
isConstructor
()
&&
!
isSynthetic
)
{
if
(
shortId
.
equals
(
enumConstructor
)
||
shortId
.
equals
(
enumConstructorAlt
))
{
mth
.
add
(
AFlag
.
DONT_GENERATE
);
}
}
else
if
(
isSynthetic
||
shortId
.
equals
(
valuesMethod
)
||
shortId
.
equals
(
valuesOfMethod
))
{
mth
.
add
(
AFlag
.
DONT_GENERATE
);
}
}
return
true
;
}
private
static
void
processEnumInnerCls
(
ConstructorInsn
co
,
EnumField
field
,
ClassNode
innerCls
)
{
...
...
@@ -190,19 +311,11 @@ public class EnumVisitor extends AbstractVisitor {
innerCls
.
add
(
AFlag
.
DONT_GENERATE
);
}
private
boolean
isEnumArrayField
(
ClassInfo
classInfo
,
FieldNode
fieldNode
)
{
if
(
fieldNode
.
getAccessFlags
().
isSynthetic
())
{
ArgType
fType
=
fieldNode
.
getType
();
return
fType
.
isArray
()
&&
fType
.
getArrayRootElement
().
equals
(
classInfo
.
getType
());
}
return
false
;
}
private
ConstructorInsn
getConstructorInsn
(
InsnNode
putInsn
)
{
if
(
putInsn
.
getArgsCount
()
!=
1
)
{
private
ConstructorInsn
getConstructorInsn
(
InsnNode
insn
)
{
if
(
insn
.
getArgsCount
()
!=
1
)
{
return
null
;
}
InsnArg
arg
=
putI
nsn
.
getArg
(
0
);
InsnArg
arg
=
i
nsn
.
getArg
(
0
);
if
(
arg
.
isInsnWrap
())
{
return
castConstructorInsn
(((
InsnWrapArg
)
arg
).
getWrapInsn
());
}
...
...
jadx-core/src/test/java/jadx/tests/api/SmaliTest.java
浏览文件 @
62ca30bb
...
...
@@ -38,6 +38,11 @@ public abstract class SmaliTest extends IntegrationTest {
return
getClassNodeFromSmaliWithPkg
(
getTestPkg
(),
getTestName
());
}
protected
ClassNode
getClassNodeFromSmaliWithClsName
(
String
fullClsName
)
{
return
getClassNodeFromSmali
(
getTestPkg
()
+
File
.
separatorChar
+
getTestName
(),
fullClsName
);
}
@Deprecated
protected
ClassNode
getClassNodeFromSmali
(
String
clsName
)
{
return
getClassNodeFromSmali
(
clsName
,
clsName
);
}
...
...
jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeAssertions.java
浏览文件 @
62ca30bb
...
...
@@ -42,14 +42,19 @@ public class JadxCodeAssertions extends AbstractStringAssert<JadxCodeAssertions>
}
String
indent
=
TestUtils
.
indent
(
commonIndent
);
StringBuilder
sb
=
new
StringBuilder
();
boolean
first
=
true
;
for
(
String
line
:
lines
)
{
if
(!
line
.
isEmpty
())
{
if
(
first
)
{
first
=
false
;
}
else
{
sb
.
append
(
CodeWriter
.
NL
);
}
sb
.
append
(
indent
);
sb
.
append
(
line
);
}
sb
.
append
(
CodeWriter
.
NL
);
}
return
co
untString
(
1
,
sb
.
toString
());
return
co
ntainsOnlyOnce
(
sb
.
toString
());
}
public
JadxCodeAssertions
print
()
{
...
...
jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums5.java
0 → 100644
浏览文件 @
62ca30bb
package
jadx.tests.integration.enums
;
import
org.junit.jupiter.api.Test
;
import
jadx.tests.api.SmaliTest
;
import
static
jadx
.
tests
.
api
.
utils
.
assertj
.
JadxAssertions
.
assertThat
;
public
class
TestEnums5
extends
SmaliTest
{
@Test
public
void
test
()
{
assertThat
(
getClassNodeFromSmaliWithClsName
(
"kotlin.collections.State"
))
.
code
()
.
containsLines
(
"enum State {"
,
indent
()
+
"Ready,"
,
indent
()
+
"NotReady,"
,
indent
()
+
"Done,"
,
indent
()
+
"Failed"
,
"}"
);
}
}
jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsInterface.java
浏览文件 @
62ca30bb
...
...
@@ -14,11 +14,13 @@ public class TestEnumsInterface extends IntegrationTest {
public
enum
Operation
implements
IOperation
{
PLUS
{
@Override
public
int
apply
(
int
x
,
int
y
)
{
return
x
+
y
;
}
},
MINUS
{
@Override
public
int
apply
(
int
x
,
int
y
)
{
return
x
-
y
;
}
...
...
jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsWithStaticFields.java
0 → 100644
浏览文件 @
62ca30bb
package
jadx.tests.integration.enums
;
import
org.junit.jupiter.api.Test
;
import
jadx.tests.api.SmaliTest
;
import
static
jadx
.
tests
.
api
.
utils
.
assertj
.
JadxAssertions
.
assertThat
;
public
class
TestEnumsWithStaticFields
extends
SmaliTest
{
@Test
public
void
test
()
{
disableCompilation
();
assertThat
(
getClassNodeFromSmali
())
.
code
()
.
containsOnlyOnce
(
"INSTANCE;"
)
.
containsOnlyOnce
(
"private static c sB;"
)
.
doesNotContain
(
" sA"
)
.
doesNotContain
(
" sC"
)
.
doesNotContain
(
"private TestEnumsWithStaticFields(String str) {"
);
}
}
jadx-core/src/test/smali/enums/TestEnums5.smali
0 → 100644
浏览文件 @
62ca30bb
.class final enum Lkotlin/collections/State;
.super Ljava/lang/Enum;
# annotations
.annotation system Ldalvik/annotation/Signature;
value = {
"Ljava/lang/Enum<",
"Lkotlin/collections/State;",
">;"
}
.end annotation
# static fields
.field private static final synthetic $VALUES:[Lkotlin/collections/State;
.field public static final enum Done:Lkotlin/collections/State;
.field public static final enum Failed:Lkotlin/collections/State;
.field public static final enum NotReady:Lkotlin/collections/State;
.field public static final enum Ready:Lkotlin/collections/State;
# direct methods
.method static constructor <clinit>()V
.registers 4
const/4 v0, 0x4
new-array v0, v0, [Lkotlin/collections/State;
new-instance v1, Lkotlin/collections/State;
const-string v2, "Ready"
const/4 v3, 0x0
invoke-direct {v1, v2, v3}, Lkotlin/collections/State;-><init>(Ljava/lang/String;I)V
sput-object v1, Lkotlin/collections/State;->Ready:Lkotlin/collections/State;
aput-object v1, v0, v3
new-instance v1, Lkotlin/collections/State;
const-string v2, "NotReady"
const/4 v3, 0x1
invoke-direct {v1, v2, v3}, Lkotlin/collections/State;-><init>(Ljava/lang/String;I)V
sput-object v1, Lkotlin/collections/State;->NotReady:Lkotlin/collections/State;
aput-object v1, v0, v3
new-instance v1, Lkotlin/collections/State;
const-string v2, "Done"
const/4 v3, 0x2
invoke-direct {v1, v2, v3}, Lkotlin/collections/State;-><init>(Ljava/lang/String;I)V
sput-object v1, Lkotlin/collections/State;->Done:Lkotlin/collections/State;
aput-object v1, v0, v3
new-instance v1, Lkotlin/collections/State;
const-string v2, "Failed"
const/4 v3, 0x3
invoke-direct {v1, v2, v3}, Lkotlin/collections/State;-><init>(Ljava/lang/String;I)V
sput-object v1, Lkotlin/collections/State;->Failed:Lkotlin/collections/State;
aput-object v1, v0, v3
sput-object v0, Lkotlin/collections/State;->$VALUES:[Lkotlin/collections/State;
return-void
.end method
.method protected constructor <init>(Ljava/lang/String;I)V
.registers 3
.param p1, "$enum_name_or_ordinal$0" # Ljava/lang/String;
.param p2, "$enum_name_or_ordinal$1" # I
.annotation system Ldalvik/annotation/Signature;
value = {
"()V"
}
.end annotation
.line 4
invoke-direct {p0, p1, p2}, Ljava/lang/Enum;-><init>(Ljava/lang/String;I)V
return-void
.end method
.method public static valueOf(Ljava/lang/String;)Lkotlin/collections/State;
.registers 2
const-class v0, Lkotlin/collections/State;
invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
move-result-object p0
check-cast p0, Lkotlin/collections/State;
return-object p0
.end method
.method public static values()[Lkotlin/collections/State;
.registers 1
sget-object v0, Lkotlin/collections/State;->$VALUES:[Lkotlin/collections/State;
invoke-virtual {v0}, [Lkotlin/collections/State;->clone()Ljava/lang/Object;
move-result-object v0
check-cast v0, [Lkotlin/collections/State;
return-object v0
.end method
jadx-core/src/test/smali/enums/TestEnumsWithStaticFields.smali
0 → 100644
浏览文件 @
62ca30bb
.class public final enum Lenums/TestEnumsWithStaticFields;
.super Ljava/lang/Enum;
.source "SourceFile"
# interfaces
.implements Lx/a/c;
# annotations
.annotation system Ldalvik/annotation/MemberClasses;
value = {
Lx/a/d$a;
}
.end annotation
.annotation system Ldalvik/annotation/Signature;
value = {
"Ljava/lang/Enum",
"<",
"Lenums/TestEnumsWithStaticFields;",
">;",
"Lx/a/c;"
}
.end annotation
# static fields
.field public static final enum sA:Lenums/TestEnumsWithStaticFields;
.field private static sB:Lx/a/c;
.field private static final synthetic sC:[Lenums/TestEnumsWithStaticFields;
# direct methods
.method static constructor <clinit>()V
.registers 4
.prologue
const v3, 0x23900
const/4 v2, 0x0
invoke-static {v3}, Lx/q;->i(I)V
.line 10
new-instance v0, Lenums/TestEnumsWithStaticFields;
const-string/jumbo v1, "INSTANCE"
invoke-direct {v0, v1}, Lenums/TestEnumsWithStaticFields;-><init>(Ljava/lang/String;)V
sput-object v0, Lenums/TestEnumsWithStaticFields;->sA:Lenums/TestEnumsWithStaticFields;
.line 9
const/4 v0, 0x1
new-array v0, v0, [Lenums/TestEnumsWithStaticFields;
sget-object v1, Lenums/TestEnumsWithStaticFields;->sA:Lenums/TestEnumsWithStaticFields;
aput-object v1, v0, v2
sput-object v0, Lenums/TestEnumsWithStaticFields;->sC:[Lenums/TestEnumsWithStaticFields;
.line 36
new-instance v0, Lx/a/d$a;
invoke-direct {v0, v2}, Lx/a/d$a;-><init>(B)V
sput-object v0, Lenums/TestEnumsWithStaticFields;->sB:Lx/a/c;
invoke-static {v3}, Lx/q;->o(I)V
return-void
.end method
.method private constructor <init>(Ljava/lang/String;)V
.registers 3
.annotation system Ldalvik/annotation/Signature;
value = {
"()V"
}
.end annotation
.prologue
.line 9
const/4 v0, 0x0
invoke-direct {p0, p1, v0}, Ljava/lang/Enum;-><init>(Ljava/lang/String;I)V
return-void
.end method
.method public static a(Lx/a/c;)V
.registers 1
.prologue
.line 79
if-eqz p0, :cond_4
.line 80
sput-object p0, Lenums/TestEnumsWithStaticFields;->sB:Lx/a/c;
.line 82
:cond_4
return-void
.end method
.method public static valueOf(Ljava/lang/String;)Lenums/TestEnumsWithStaticFields;
.registers 3
.prologue
const v1, 0x238f8
invoke-static {v1}, Lx/q;->i(I)V
.line 9
const-class v0, Lenums/TestEnumsWithStaticFields;
invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
move-result-object v0
check-cast v0, Lenums/TestEnumsWithStaticFields;
invoke-static {v1}, Lx/q;->o(I)V
return-object v0
.end method
.method public static values()[Lenums/TestEnumsWithStaticFields;
.registers 2
.prologue
const v1, 0x238f7
invoke-static {v1}, Lx/q;->i(I)V
.line 9
sget-object v0, Lenums/TestEnumsWithStaticFields;->sC:[Lenums/TestEnumsWithStaticFields;
invoke-virtual {v0}, [Lenums/TestEnumsWithStaticFields;->clone()Ljava/lang/Object;
move-result-object v0
check-cast v0, [Lenums/TestEnumsWithStaticFields;
invoke-static {v1}, Lx/q;->o(I)V
return-object v0
.end method
# virtual methods
.method public final FR(I)V
.registers 4
.prologue
const v1, 0x238fb
invoke-static {v1}, Lx/q;->i(I)V
.line 96
sget-object v0, Lenums/TestEnumsWithStaticFields;->sB:Lx/a/c;
invoke-interface {v0, p1}, Lx/a/c;->FR(I)V
.line 97
invoke-static {v1}, Lx/q;->o(I)V
return-void
.end method
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录