Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
keyescgm
jadx
提交
6192ced2
J
jadx
项目概览
keyescgm
/
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,发现更多精彩内容 >>
提交
6192ced2
编写于
6月 01, 2020
作者:
S
Skylot
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix: improve type inference of type variables in method invoke (#913)
上级
ae31fee8
变更
14
隐藏空白更改
内联
并排
Showing
14 changed file
with
316 addition
and
99 deletion
+316
-99
jadx-core/src/main/java/jadx/core/Consts.java
jadx-core/src/main/java/jadx/core/Consts.java
+1
-0
jadx-core/src/main/java/jadx/core/dex/attributes/AType.java
jadx-core/src/main/java/jadx/core/dex/attributes/AType.java
+2
-0
jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodTypeVarsAttr.java
...va/jadx/core/dex/attributes/nodes/MethodTypeVarsAttr.java
+33
-0
jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java
...rc/main/java/jadx/core/dex/instructions/args/ArgType.java
+31
-0
jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java
jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java
+11
-0
jadx-core/src/main/java/jadx/core/dex/nodes/utils/TypeUtils.java
...re/src/main/java/jadx/core/dex/nodes/utils/TypeUtils.java
+37
-2
jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoApplyVisitor.java
...dx/core/dex/visitors/debuginfo/DebugInfoApplyVisitor.java
+1
-1
jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java
...core/dex/visitors/typeinference/TypeInferenceVisitor.java
+40
-37
jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeSearch.java
...java/jadx/core/dex/visitors/typeinference/TypeSearch.java
+3
-3
jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdate.java
...java/jadx/core/dex/visitors/typeinference/TypeUpdate.java
+77
-40
jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdateInfo.java
.../jadx/core/dex/visitors/typeinference/TypeUpdateInfo.java
+8
-1
jadx-core/src/test/java/jadx/tests/integration/invoke/TestCastInOverloadedInvoke.java
.../tests/integration/invoke/TestCastInOverloadedInvoke.java
+1
-4
jadx-core/src/test/java/jadx/tests/integration/invoke/TestHierarchyOverloadedInvoke.java
...sts/integration/invoke/TestHierarchyOverloadedInvoke.java
+1
-11
jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics6.java
...test/java/jadx/tests/integration/types/TestGenerics6.java
+70
-0
未找到文件。
jadx-core/src/main/java/jadx/core/Consts.java
浏览文件 @
6192ced2
...
...
@@ -3,6 +3,7 @@ package jadx.core;
public
class
Consts
{
public
static
final
boolean
DEBUG
=
false
;
public
static
final
boolean
DEBUG_USAGE
=
false
;
public
static
final
boolean
DEBUG_TYPE_INFERENCE
=
false
;
public
static
final
String
CLASS_OBJECT
=
"java.lang.Object"
;
public
static
final
String
CLASS_STRING
=
"java.lang.String"
;
...
...
jadx-core/src/main/java/jadx/core/dex/attributes/AType.java
浏览文件 @
6192ced2
...
...
@@ -21,6 +21,7 @@ import jadx.core.dex.attributes.nodes.LoopInfo;
import
jadx.core.dex.attributes.nodes.LoopLabelAttr
;
import
jadx.core.dex.attributes.nodes.MethodInlineAttr
;
import
jadx.core.dex.attributes.nodes.MethodOverrideAttr
;
import
jadx.core.dex.attributes.nodes.MethodTypeVarsAttr
;
import
jadx.core.dex.attributes.nodes.PhiListAttr
;
import
jadx.core.dex.attributes.nodes.RegDebugInfoAttr
;
import
jadx.core.dex.attributes.nodes.RenameReasonAttr
;
...
...
@@ -64,6 +65,7 @@ public class AType<T extends IAttribute> {
public
static
final
AType
<
MethodParameters
>
ANNOTATION_MTH_PARAMETERS
=
new
AType
<>();
public
static
final
AType
<
SkipMethodArgsAttr
>
SKIP_MTH_ARGS
=
new
AType
<>();
public
static
final
AType
<
MethodOverrideAttr
>
METHOD_OVERRIDE
=
new
AType
<>();
public
static
final
AType
<
MethodTypeVarsAttr
>
METHOD_TYPE_VARS
=
new
AType
<>();
// region
public
static
final
AType
<
DeclareVariablesAttr
>
DECLARE_VARIABLES
=
new
AType
<>();
...
...
jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodTypeVarsAttr.java
0 → 100644
浏览文件 @
6192ced2
package
jadx.core.dex.attributes.nodes
;
import
java.util.Set
;
import
jadx.core.dex.attributes.AType
;
import
jadx.core.dex.attributes.IAttribute
;
import
jadx.core.dex.instructions.args.ArgType
;
/**
* Set of known type variables at current method
*/
public
class
MethodTypeVarsAttr
implements
IAttribute
{
private
final
Set
<
ArgType
>
typeVars
;
public
MethodTypeVarsAttr
(
Set
<
ArgType
>
typeVars
)
{
this
.
typeVars
=
typeVars
;
}
public
Set
<
ArgType
>
getTypeVars
()
{
return
typeVars
;
}
@Override
public
AType
<
MethodTypeVarsAttr
>
getType
()
{
return
AType
.
METHOD_TYPE_VARS
;
}
@Override
public
String
toString
()
{
return
"TYPE_VARS: "
+
typeVars
;
}
}
jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java
浏览文件 @
6192ced2
...
...
@@ -4,6 +4,7 @@ import java.util.Arrays;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Objects
;
import
java.util.function.Function
;
import
org.jetbrains.annotations.NotNull
;
...
...
@@ -744,6 +745,36 @@ public abstract class ArgType {
return
false
;
}
/**
* Recursively visit all subtypes of this type.
* To exit return non-null value.
*/
public
<
R
>
R
visitTypes
(
Function
<
ArgType
,
R
>
visitor
)
{
R
r
=
visitor
.
apply
(
this
);
if
(
r
!=
null
)
{
return
r
;
}
ArgType
wildcardType
=
getWildcardType
();
if
(
wildcardType
!=
null
)
{
return
wildcardType
.
visitTypes
(
visitor
);
}
if
(
isArray
())
{
ArgType
arrayElement
=
getArrayElement
();
if
(
arrayElement
!=
null
)
{
return
arrayElement
.
visitTypes
(
visitor
);
}
}
if
(
isGeneric
())
{
ArgType
[]
genericTypes
=
getGenericTypes
();
if
(
genericTypes
!=
null
)
{
for
(
ArgType
genericType
:
genericTypes
)
{
return
genericType
.
visitTypes
(
visitor
);
}
}
}
return
null
;
}
public
static
ArgType
tryToResolveClassAlias
(
RootNode
root
,
ArgType
type
)
{
if
(!
type
.
isObject
()
||
type
.
isGenericType
())
{
return
type
;
...
...
jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java
浏览文件 @
6192ced2
...
...
@@ -10,6 +10,7 @@ import java.util.List;
import
java.util.Map
;
import
java.util.Objects
;
import
java.util.Set
;
import
java.util.function.Consumer
;
import
java.util.stream.Collectors
;
import
org.jetbrains.annotations.NotNull
;
...
...
@@ -425,6 +426,16 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
return
parent
==
this
?
this
:
parent
.
getTopParentClass
();
}
public
void
visitParentClasses
(
Consumer
<
ClassNode
>
consumer
)
{
ClassNode
currentCls
=
this
;
ClassNode
parentCls
=
currentCls
.
getParentClass
();
while
(
parentCls
!=
currentCls
)
{
consumer
.
accept
(
parentCls
);
currentCls
=
parentCls
;
parentCls
=
currentCls
.
getParentClass
();
}
}
public
boolean
hasNotGeneratedParent
()
{
if
(
contains
(
AFlag
.
DONT_GENERATE
))
{
return
true
;
...
...
jadx-core/src/main/java/jadx/core/dex/nodes/utils/TypeUtils.java
浏览文件 @
6192ced2
...
...
@@ -2,19 +2,23 @@ package jadx.core.dex.nodes.utils;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
org.jetbrains.annotations.NotNull
;
import
org.jetbrains.annotations.Nullable
;
import
jadx.core.clsp.ClspClass
;
import
jadx.core.dex.attributes.AType
;
import
jadx.core.dex.attributes.nodes.MethodTypeVarsAttr
;
import
jadx.core.dex.instructions.BaseInvokeNode
;
import
jadx.core.dex.instructions.args.ArgType
;
import
jadx.core.dex.instructions.args.InsnArg
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.core.dex.nodes.GenericTypeParameter
;
import
jadx.core.dex.nodes.IMethodDetails
;
import
jadx.core.dex.nodes.MethodNode
;
import
jadx.core.dex.nodes.RootNode
;
public
class
TypeUtils
{
...
...
@@ -24,7 +28,6 @@ public class TypeUtils {
this
.
root
=
rootNode
;
}
@NotNull
public
List
<
GenericTypeParameter
>
getClassGenerics
(
ArgType
type
)
{
ClassNode
classNode
=
root
.
resolveClass
(
type
);
if
(
classNode
!=
null
)
{
...
...
@@ -38,6 +41,38 @@ public class TypeUtils {
return
generics
==
null
?
Collections
.
emptyList
()
:
generics
;
}
public
Set
<
ArgType
>
getKnownTypeVarsAtMethod
(
MethodNode
mth
)
{
MethodTypeVarsAttr
typeVarsAttr
=
mth
.
get
(
AType
.
METHOD_TYPE_VARS
);
if
(
typeVarsAttr
!=
null
)
{
return
typeVarsAttr
.
getTypeVars
();
}
Set
<
ArgType
>
typeVars
=
collectKnownTypeVarsAtMethod
(
mth
);
mth
.
addAttr
(
new
MethodTypeVarsAttr
(
typeVars
));
return
typeVars
;
}
private
static
Set
<
ArgType
>
collectKnownTypeVarsAtMethod
(
MethodNode
mth
)
{
Set
<
ArgType
>
typeVars
=
new
HashSet
<>();
ClassNode
declCls
=
mth
.
getParentClass
();
addTypeVarsFromCls
(
typeVars
,
declCls
);
declCls
.
visitParentClasses
(
parent
->
addTypeVarsFromCls
(
typeVars
,
parent
));
for
(
GenericTypeParameter
typeParameter
:
mth
.
getTypeParameters
())
{
typeVars
.
add
(
typeParameter
.
getTypeVariable
());
}
return
typeVars
.
isEmpty
()
?
Collections
.
emptySet
()
:
typeVars
;
}
private
static
void
addTypeVarsFromCls
(
Set
<
ArgType
>
typeVars
,
ClassNode
parentCls
)
{
List
<
GenericTypeParameter
>
typeParameters
=
parentCls
.
getGenericTypeParameters
();
if
(
typeParameters
.
isEmpty
())
{
return
;
}
for
(
GenericTypeParameter
typeParameter
:
typeParameters
)
{
typeVars
.
add
(
typeParameter
.
getTypeVariable
());
}
}
/**
* Replace generic types in {@code typeWithGeneric} using instance types
* <br>
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoApplyVisitor.java
浏览文件 @
6192ced2
...
...
@@ -143,7 +143,7 @@ public class DebugInfoApplyVisitor extends AbstractVisitor {
}
public
static
void
applyDebugInfo
(
MethodNode
mth
,
SSAVar
ssaVar
,
ArgType
type
,
String
varName
)
{
TypeUpdateResult
result
=
mth
.
root
().
getTypeUpdate
().
applyWithWiderAllow
(
ssaVar
,
type
);
TypeUpdateResult
result
=
mth
.
root
().
getTypeUpdate
().
applyWithWiderAllow
(
mth
,
ssaVar
,
type
);
if
(
result
==
TypeUpdateResult
.
REJECT
)
{
if
(
Consts
.
DEBUG
)
{
LOG
.
debug
(
"Reject debug info of type: {} and name: '{}' for {}, mth: {}"
,
type
,
varName
,
ssaVar
,
mth
);
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java
浏览文件 @
6192ced2
...
...
@@ -72,7 +72,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
if
(
mth
.
isNoCode
())
{
return
;
}
if
(
Consts
.
DEBUG
)
{
if
(
Consts
.
DEBUG
_TYPE_INFERENCE
)
{
LOG
.
info
(
"Start type inference in method: {}"
,
mth
);
}
if
(
resolveTypes
(
mth
))
{
...
...
@@ -103,20 +103,21 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
/**
* Guess type from usage and try to set it to current variable
* and all connected instructions with {@link TypeUpdate#apply(SSAVar, ArgType)}
* and all connected instructions with {@link TypeUpdate#apply(
MethodNode,
SSAVar, ArgType)}
*/
private
boolean
runTypePropagation
(
MethodNode
mth
)
{
List
<
SSAVar
>
ssaVars
=
mth
.
getSVars
();
// collect initial type bounds from assign and usages`
mth
.
getSVars
()
.
forEach
(
this
::
attachBounds
);
mth
.
getSVars
()
.
forEach
(
this
::
mergePhiBounds
);
ssaVars
.
forEach
(
this
::
attachBounds
);
ssaVars
.
forEach
(
this
::
mergePhiBounds
);
// start initial type propagation
mth
.
getSVars
().
forEach
(
this
::
setImmutableType
);
mth
.
getSVars
().
forEach
(
this
::
setBestType
);
ssaVars
.
forEach
(
var
->
setImmutableType
(
mth
,
var
)
);
ssaVars
.
forEach
(
var
->
setBestType
(
mth
,
var
)
);
// try other types if type is still unknown
boolean
resolved
=
true
;
for
(
SSAVar
var
:
mth
.
getSVars
()
)
{
for
(
SSAVar
var
:
ssaVars
)
{
ArgType
type
=
var
.
getTypeInfo
().
getType
();
if
(!
type
.
isTypeKnown
()
&&
!
var
.
isTypeImmutable
()
...
...
@@ -131,7 +132,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
TypeSearch
typeSearch
=
new
TypeSearch
(
mth
);
try
{
if
(!
typeSearch
.
run
())
{
mth
.
addWarn
(
"Multi-variable type inference failed"
);
mth
.
addWarn
Comment
(
"Multi-variable type inference failed"
);
}
for
(
SSAVar
var
:
mth
.
getSVars
())
{
if
(!
var
.
getTypeInfo
().
getType
().
isTypeKnown
())
{
...
...
@@ -140,50 +141,44 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
}
return
true
;
}
catch
(
Exception
e
)
{
mth
.
addWarn
(
"Multi-variable type inference failed. Error: "
+
Utils
.
getStackTrace
(
e
));
mth
.
addWarn
Comment
(
"Multi-variable type inference failed. Error: "
+
Utils
.
getStackTrace
(
e
));
return
false
;
}
}
private
boolean
setImmutableType
(
SSAVar
ssaVar
)
{
private
void
setImmutableType
(
MethodNode
mth
,
SSAVar
ssaVar
)
{
try
{
ArgType
immutableType
=
ssaVar
.
getImmutableType
();
if
(
immutableType
!=
null
)
{
return
applyImmutableType
(
ssaVar
,
immutableType
);
applyImmutableType
(
mth
,
ssaVar
,
immutableType
);
}
return
false
;
}
catch
(
Exception
e
)
{
LOG
.
error
(
"Failed to set immutable type for var: {}"
,
ssaVar
,
e
);
return
false
;
}
}
private
boolean
setBestType
(
SSAVar
ssaVar
)
{
private
boolean
setBestType
(
MethodNode
mth
,
SSAVar
ssaVar
)
{
try
{
return
calculateFromBounds
(
ssaVar
);
return
calculateFromBounds
(
mth
,
ssaVar
);
}
catch
(
Exception
e
)
{
LOG
.
error
(
"Failed to calculate best type for var: {}"
,
ssaVar
,
e
);
return
false
;
}
}
private
boolean
applyImmutableType
(
SSAVar
ssaVar
,
ArgType
initType
)
{
TypeUpdateResult
result
=
typeUpdate
.
apply
(
ssaVar
,
initType
);
if
(
result
==
TypeUpdateResult
.
REJECT
)
{
if
(
Consts
.
DEBUG
)
{
LOG
.
info
(
"Reject initial immutable type {} for {}"
,
initType
,
ssaVar
);
}
return
false
;
private
void
applyImmutableType
(
MethodNode
mth
,
SSAVar
ssaVar
,
ArgType
initType
)
{
TypeUpdateResult
result
=
typeUpdate
.
apply
(
mth
,
ssaVar
,
initType
);
if
(
Consts
.
DEBUG_TYPE_INFERENCE
&&
result
==
TypeUpdateResult
.
REJECT
)
{
LOG
.
info
(
"Reject initial immutable type {} for {}"
,
initType
,
ssaVar
);
}
return
result
==
TypeUpdateResult
.
CHANGED
;
}
private
boolean
calculateFromBounds
(
SSAVar
ssaVar
)
{
private
boolean
calculateFromBounds
(
MethodNode
mth
,
SSAVar
ssaVar
)
{
TypeInfo
typeInfo
=
ssaVar
.
getTypeInfo
();
Set
<
ITypeBound
>
bounds
=
typeInfo
.
getBounds
();
Optional
<
ArgType
>
bestTypeOpt
=
selectBestTypeFromBounds
(
bounds
);
if
(!
bestTypeOpt
.
isPresent
())
{
if
(
Consts
.
DEBUG
)
{
if
(
Consts
.
DEBUG
_TYPE_INFERENCE
)
{
LOG
.
warn
(
"Failed to select best type from bounds, count={} : "
,
bounds
.
size
());
for
(
ITypeBound
bound
:
bounds
)
{
LOG
.
warn
(
" {}"
,
bound
);
...
...
@@ -192,9 +187,9 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
return
false
;
}
ArgType
candidateType
=
bestTypeOpt
.
get
();
TypeUpdateResult
result
=
typeUpdate
.
apply
(
ssaVar
,
candidateType
);
TypeUpdateResult
result
=
typeUpdate
.
apply
(
mth
,
ssaVar
,
candidateType
);
if
(
result
==
TypeUpdateResult
.
REJECT
)
{
if
(
Consts
.
DEBUG
)
{
if
(
Consts
.
DEBUG
_TYPE_INFERENCE
)
{
if
(
ssaVar
.
getTypeInfo
().
getType
().
equals
(
candidateType
))
{
LOG
.
info
(
"Same type rejected: {} -> {}, bounds: {}"
,
ssaVar
,
candidateType
,
bounds
);
}
else
if
(
candidateType
.
isTypeKnown
())
{
...
...
@@ -235,7 +230,11 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
}
private
void
addBound
(
TypeInfo
typeInfo
,
ITypeBound
bound
)
{
if
(
bound
!=
null
&&
bound
.
getType
()
!=
ArgType
.
UNKNOWN
)
{
if
(
bound
==
null
)
{
return
;
}
if
(
bound
instanceof
ITypeBoundDynamic
||
bound
.
getType
()
!=
ArgType
.
UNKNOWN
)
{
typeInfo
.
getBounds
().
add
(
bound
);
}
}
...
...
@@ -333,10 +332,10 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
return
new
TypeBoundInvokeUse
(
root
,
invoke
,
regArg
,
argType
);
}
private
boolean
tryPossibleTypes
(
SSAVar
var
,
ArgType
type
)
{
private
boolean
tryPossibleTypes
(
MethodNode
mth
,
SSAVar
var
,
ArgType
type
)
{
List
<
ArgType
>
types
=
makePossibleTypesList
(
type
);
for
(
ArgType
candidateType
:
types
)
{
TypeUpdateResult
result
=
typeUpdate
.
apply
(
var
,
candidateType
);
TypeUpdateResult
result
=
typeUpdate
.
apply
(
mth
,
var
,
candidateType
);
if
(
result
==
TypeUpdateResult
.
CHANGED
)
{
return
true
;
}
...
...
@@ -362,11 +361,11 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
private
boolean
tryDeduceType
(
MethodNode
mth
,
SSAVar
var
,
@Nullable
ArgType
type
)
{
// try best type from bounds again
if
(
setBestType
(
var
))
{
if
(
setBestType
(
mth
,
var
))
{
return
true
;
}
// try all possible types (useful for primitives)
if
(
type
!=
null
&&
tryPossibleTypes
(
var
,
type
))
{
if
(
type
!=
null
&&
tryPossibleTypes
(
mth
,
var
,
type
))
{
return
true
;
}
// for objects try super types
...
...
@@ -412,7 +411,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
private
boolean
checkRawType
(
MethodNode
mth
,
SSAVar
var
,
ArgType
objType
)
{
if
(
objType
.
isObject
()
&&
objType
.
containsGeneric
())
{
ArgType
rawType
=
ArgType
.
object
(
objType
.
getObject
());
TypeUpdateResult
result
=
typeUpdate
.
applyWithWiderAllow
(
var
,
rawType
);
TypeUpdateResult
result
=
typeUpdate
.
applyWithWiderAllow
(
mth
,
var
,
rawType
);
return
result
==
TypeUpdateResult
.
CHANGED
;
}
return
false
;
...
...
@@ -575,7 +574,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
for
(
ArgType
objType
:
objTypes
)
{
for
(
String
ancestor
:
clsp
.
getSuperTypes
(
objType
.
getObject
()))
{
ArgType
ancestorType
=
ArgType
.
object
(
ancestor
);
TypeUpdateResult
result
=
typeUpdate
.
applyWithWiderAllow
(
var
,
ancestorType
);
TypeUpdateResult
result
=
typeUpdate
.
applyWithWiderAllow
(
mth
,
var
,
ancestorType
);
if
(
result
==
TypeUpdateResult
.
CHANGED
)
{
return
true
;
}
...
...
@@ -588,7 +587,9 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
if
(
var
.
getTypeInfo
().
getType
()
==
ArgType
.
BOOLEAN
)
{
for
(
ITypeBound
bound
:
var
.
getTypeInfo
().
getBounds
())
{
if
(
bound
.
getBound
()
==
BoundEnum
.
USE
&&
bound
.
getType
().
isPrimitive
()
&&
bound
.
getType
()
!=
ArgType
.
BOOLEAN
)
{
&&
bound
.
getType
().
isPrimitive
()
&&
bound
.
getType
()
!=
ArgType
.
BOOLEAN
&&
bound
.
getArg
()
!=
null
)
{
InsnNode
insn
=
bound
.
getArg
().
getParentInsn
();
if
(
insn
==
null
||
insn
.
getType
()
==
InsnType
.
CAST
)
{
continue
;
...
...
@@ -612,8 +613,10 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
}
BlockNode
blockNode
=
BlockUtils
.
getBlockByInsn
(
mth
,
insn
);
List
<
InsnNode
>
insnList
=
blockNode
.
getInstructions
();
insnList
.
add
(
insnList
.
indexOf
(
insn
),
castNode
);
if
(
blockNode
!=
null
)
{
List
<
InsnNode
>
insnList
=
blockNode
.
getInstructions
();
insnList
.
add
(
insnList
.
indexOf
(
insn
),
castNode
);
}
}
}
}
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeSearch.java
浏览文件 @
6192ced2
...
...
@@ -63,7 +63,7 @@ public class TypeSearch {
}
else
{
search
(
vars
);
searchSuccess
=
fullCheck
(
vars
);
if
(
Consts
.
DEBUG
&&
!
searchSuccess
)
{
if
(
Consts
.
DEBUG
_TYPE_INFERENCE
&&
!
searchSuccess
)
{
LOG
.
warn
(
"Multi-variable search failed in {}"
,
mth
);
}
}
...
...
@@ -86,7 +86,7 @@ public class TypeSearch {
// exclude unknown variables
continue
;
}
TypeUpdateResult
res
=
typeUpdate
.
applyWithWiderIgnSame
(
var
.
getVar
(),
var
.
getCurrentType
());
TypeUpdateResult
res
=
typeUpdate
.
applyWithWiderIgnSame
(
mth
,
var
.
getVar
(),
var
.
getCurrentType
());
if
(
res
==
TypeUpdateResult
.
REJECT
)
{
mth
.
addComment
(
"JADX DEBUG: Multi-variable search result rejected for "
+
var
);
applySuccess
=
false
;
...
...
@@ -97,7 +97,7 @@ public class TypeSearch {
private
boolean
search
(
List
<
TypeSearchVarInfo
>
vars
)
{
int
len
=
vars
.
size
();
if
(
Consts
.
DEBUG
)
{
if
(
Consts
.
DEBUG
_TYPE_INFERENCE
)
{
LOG
.
debug
(
"Run search for {} vars: "
,
len
);
StringBuilder
sb
=
new
StringBuilder
();
long
count
=
1
;
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdate.java
浏览文件 @
6192ced2
...
...
@@ -5,6 +5,8 @@ import java.util.List;
import
java.util.Map
;
import
java.util.Objects
;
import
java.util.Set
;
import
java.util.function.Function
;
import
java.util.function.Supplier
;
import
org.jetbrains.annotations.NotNull
;
import
org.jetbrains.annotations.Nullable
;
...
...
@@ -22,6 +24,7 @@ import jadx.core.dex.instructions.args.RegisterArg;
import
jadx.core.dex.instructions.args.SSAVar
;
import
jadx.core.dex.nodes.IMethodDetails
;
import
jadx.core.dex.nodes.InsnNode
;
import
jadx.core.dex.nodes.MethodNode
;
import
jadx.core.dex.nodes.RootNode
;
import
jadx.core.dex.nodes.utils.TypeUtils
;
import
jadx.core.utils.exceptions.JadxOverflowException
;
...
...
@@ -47,30 +50,30 @@ public final class TypeUpdate {
/**
* Perform recursive type checking and type propagation for all related variables
*/
public
TypeUpdateResult
apply
(
SSAVar
ssaVar
,
ArgType
candidateType
)
{
return
apply
(
ssaVar
,
candidateType
,
TypeUpdateFlags
.
FLAGS_EMPTY
);
public
TypeUpdateResult
apply
(
MethodNode
mth
,
SSAVar
ssaVar
,
ArgType
candidateType
)
{
return
apply
(
mth
,
ssaVar
,
candidateType
,
TypeUpdateFlags
.
FLAGS_EMPTY
);
}
/**
* Allow wider types for apply from debug info and some special cases
*/
public
TypeUpdateResult
applyWithWiderAllow
(
SSAVar
ssaVar
,
ArgType
candidateType
)
{
return
apply
(
ssaVar
,
candidateType
,
TypeUpdateFlags
.
FLAGS_WIDER
);
public
TypeUpdateResult
applyWithWiderAllow
(
MethodNode
mth
,
SSAVar
ssaVar
,
ArgType
candidateType
)
{
return
apply
(
mth
,
ssaVar
,
candidateType
,
TypeUpdateFlags
.
FLAGS_WIDER
);
}
/**
* Force type setting
*/
public
TypeUpdateResult
applyWithWiderIgnSame
(
SSAVar
ssaVar
,
ArgType
candidateType
)
{
return
apply
(
ssaVar
,
candidateType
,
TypeUpdateFlags
.
FLAGS_WIDER_IGNSAME
);
public
TypeUpdateResult
applyWithWiderIgnSame
(
MethodNode
mth
,
SSAVar
ssaVar
,
ArgType
candidateType
)
{
return
apply
(
mth
,
ssaVar
,
candidateType
,
TypeUpdateFlags
.
FLAGS_WIDER_IGNSAME
);
}
private
TypeUpdateResult
apply
(
SSAVar
ssaVar
,
ArgType
candidateType
,
TypeUpdateFlags
flags
)
{
private
TypeUpdateResult
apply
(
MethodNode
mth
,
SSAVar
ssaVar
,
ArgType
candidateType
,
TypeUpdateFlags
flags
)
{
if
(
candidateType
==
null
||
!
candidateType
.
isTypeKnown
())
{
return
REJECT
;
}
TypeUpdateInfo
updateInfo
=
new
TypeUpdateInfo
(
flags
);
TypeUpdateInfo
updateInfo
=
new
TypeUpdateInfo
(
mth
,
flags
);
TypeUpdateResult
result
=
updateTypeChecked
(
updateInfo
,
ssaVar
.
getAssign
(),
candidateType
);
if
(
result
==
REJECT
)
{
return
result
;
...
...
@@ -79,7 +82,7 @@ public final class TypeUpdate {
if
(
updates
.
isEmpty
())
{
return
SAME
;
}
if
(
Consts
.
DEBUG
)
{
if
(
Consts
.
DEBUG
_TYPE_INFERENCE
)
{
LOG
.
debug
(
"Applying types for {} -> {}"
,
ssaVar
,
candidateType
);
updates
.
forEach
(
updateEntry
->
LOG
.
debug
(
" {} -> {}, insn: {}"
,
updateEntry
.
getType
(),
updateEntry
.
getArg
(),
updateEntry
.
getArg
().
getParentInsn
()));
...
...
@@ -102,13 +105,13 @@ public final class TypeUpdate {
if
(
compareResult
==
TypeCompareEnum
.
EQUAL
)
{
return
SAME
;
}
if
(
Consts
.
DEBUG
)
{
if
(
Consts
.
DEBUG
_TYPE_INFERENCE
)
{
LOG
.
debug
(
"Type rejected for {} due to conflict: candidate={}, current={}"
,
arg
,
candidateType
,
currentType
);
}
return
REJECT
;
}
if
(
compareResult
.
isWider
()
&&
!
updateInfo
.
getFlags
().
isAllowWider
())
{
if
(
Consts
.
DEBUG
)
{
if
(
Consts
.
DEBUG
_TYPE_INFERENCE
)
{
LOG
.
debug
(
"Type rejected for {}: candidate={} is wider than current={}"
,
arg
,
candidateType
,
currentType
);
}
return
REJECT
;
...
...
@@ -124,13 +127,13 @@ public final class TypeUpdate {
TypeInfo
typeInfo
=
ssaVar
.
getTypeInfo
();
ArgType
immutableType
=
ssaVar
.
getImmutableType
();
if
(
immutableType
!=
null
&&
!
Objects
.
equals
(
immutableType
,
candidateType
))
{
if
(
Consts
.
DEBUG
)
{
if
(
Consts
.
DEBUG
_TYPE_INFERENCE
)
{
LOG
.
info
(
"Reject change immutable type {} to {} for {}"
,
immutableType
,
candidateType
,
ssaVar
);
}
return
REJECT
;
}
if
(!
inBounds
(
updateInfo
,
typeInfo
.
getBounds
(),
candidateType
))
{
if
(
Consts
.
DEBUG
)
{
if
(
Consts
.
DEBUG
_TYPE_INFERENCE
)
{
LOG
.
debug
(
"Reject type '{}' for {} by bounds: {}"
,
candidateType
,
ssaVar
,
typeInfo
.
getBounds
());
}
return
REJECT
;
...
...
@@ -164,7 +167,7 @@ public final class TypeUpdate {
}
updateInfo
.
requestUpdate
(
arg
,
candidateType
);
if
(
updateInfo
.
getUpdates
().
size
()
>
500
)
{
if
(
Consts
.
DEBUG
)
{
if
(
Consts
.
DEBUG
_TYPE_INFERENCE
)
{
LOG
.
error
(
"Type update error: too deep update tree"
);
}
return
REJECT
;
...
...
@@ -287,53 +290,87 @@ public final class TypeUpdate {
// TODO: implement backward type propagation (from result to instance)
return
SAME
;
}
if
(
invoke
.
getInstanceArg
()
==
arg
&&
candidateType
.
containsGeneric
())
{
// resolve result and arg types from generic instance type
if
(
invoke
.
getInstanceArg
()
==
arg
)
{
IMethodDetails
methodDetails
=
root
.
getMethodUtils
().
getMethodDetails
(
invoke
);
if
(
methodDetails
==
null
)
{
return
SAME
;
}
TypeUtils
typeUtils
=
root
.
getTypeUtils
();
Set
<
ArgType
>
knownTypeVars
=
typeUtils
.
getKnownTypeVarsAtMethod
(
updateInfo
.
getMth
());
Map
<
ArgType
,
ArgType
>
typeVarsMap
=
typeUtils
.
getTypeVariablesMapping
(
candidateType
);
ArgType
returnType
=
methodDetails
.
getReturnType
();
List
<
ArgType
>
argTypes
=
methodDetails
.
getArgTypes
();
int
argsCount
=
argTypes
.
size
();
if
(
typeVarsMap
.
isEmpty
())
{
return
SAME
;
// generics can't be resolved => use as is
return
applyInvokeTypes
(
updateInfo
,
invoke
,
argsCount
,
knownTypeVars
,
()
->
returnType
,
argTypes:
:
get
);
}
// resolve types before apply
return
applyInvokeTypes
(
updateInfo
,
invoke
,
argsCount
,
knownTypeVars
,
()
->
typeUtils
.
replaceTypeVariablesUsingMap
(
returnType
,
typeVarsMap
),
argNum
->
typeUtils
.
replaceClassGenerics
(
candidateType
,
argTypes
.
get
(
argNum
)));
}
return
SAME
;
boolean
allSame
=
true
;
if
(
invoke
.
getResult
()
!=
null
)
{
ArgType
returnType
=
typeUtils
.
replaceTypeVariablesUsingMap
(
methodDetails
.
getReturnType
(),
typeVarsMap
);
if
(
returnType
!=
null
)
{
TypeUpdateResult
result
=
updateTypeChecked
(
updateInfo
,
invoke
.
getResult
(),
returnType
);
if
(
result
==
REJECT
)
{
}
private
TypeUpdateResult
applyInvokeTypes
(
TypeUpdateInfo
updateInfo
,
BaseInvokeNode
invoke
,
int
argsCount
,
Set
<
ArgType
>
knownTypeVars
,
Supplier
<
ArgType
>
getReturnType
,
Function
<
Integer
,
ArgType
>
getArgType
)
{
boolean
allSame
=
true
;
RegisterArg
resultArg
=
invoke
.
getResult
();
if
(
resultArg
!=
null
&&
!
resultArg
.
isTypeImmutable
())
{
ArgType
returnType
=
checkType
(
knownTypeVars
,
getReturnType
.
get
());
if
(
returnType
!=
null
)
{
TypeUpdateResult
result
=
updateTypeChecked
(
updateInfo
,
resultArg
,
returnType
);
if
(
result
==
REJECT
)
{
TypeCompareEnum
compare
=
comparator
.
compareTypes
(
returnType
,
resultArg
.
getType
());
if
(
compare
.
isWider
())
{
return
REJECT
;
}
if
(
result
==
CHANGED
)
{
allSame
=
false
;
}
}
if
(
result
==
CHANGED
)
{
allSame
=
false
;
}
}
int
argOffset
=
invoke
.
getFirstArgOffset
();
List
<
ArgType
>
argTypes
=
methodDetails
.
getArgTypes
();
int
argsCount
=
argTypes
.
size
();
for
(
int
i
=
0
;
i
<
argsCount
;
i
++)
{
ArgType
genericArgType
=
argTypes
.
get
(
i
);
ArgType
resultArgType
=
typeUtils
.
replaceClassGenerics
(
candidateType
,
genericArgType
);
if
(
resultArgType
!=
null
)
{
InsnArg
invokeArg
=
invoke
.
getArg
(
argOffset
+
i
);
TypeUpdateResult
result
=
updateTypeChecked
(
updateInfo
,
invokeArg
,
resultArgType
);
}
int
argOffset
=
invoke
.
getFirstArgOffset
();
for
(
int
i
=
0
;
i
<
argsCount
;
i
++)
{
InsnArg
invokeArg
=
invoke
.
getArg
(
argOffset
+
i
);
if
(!
invokeArg
.
isTypeImmutable
())
{
ArgType
argType
=
checkType
(
knownTypeVars
,
getArgType
.
apply
(
i
));
if
(
argType
!=
null
)
{
TypeUpdateResult
result
=
updateTypeChecked
(
updateInfo
,
invokeArg
,
argType
);
if
(
result
==
REJECT
)
{
return
REJECT
;
TypeCompareEnum
compare
=
comparator
.
compareTypes
(
argType
,
invokeArg
.
getType
());
if
(
compare
.
isNarrow
())
{
return
REJECT
;
}
}
if
(
result
==
CHANGED
)
{
allSame
=
false
;
}
}
}
return
allSame
?
SAME
:
CHANGED
;
}
return
SAME
;
return
allSame
?
SAME
:
CHANGED
;
}
@Nullable
private
ArgType
checkType
(
Set
<
ArgType
>
knownTypeVars
,
@Nullable
ArgType
type
)
{
if
(
type
==
null
)
{
return
null
;
}
if
(
type
.
containsTypeVariable
())
{
if
(
knownTypeVars
.
isEmpty
())
{
return
null
;
}
Boolean
hasUnknown
=
type
.
visitTypes
(
t
->
t
.
isGenericType
()
&&
!
knownTypeVars
.
contains
(
t
)
?
Boolean
.
TRUE
:
null
);
if
(
hasUnknown
!=
null
)
{
return
null
;
}
}
return
type
;
}
private
TypeUpdateResult
sameFirstArgListener
(
TypeUpdateInfo
updateInfo
,
InsnNode
insn
,
InsnArg
arg
,
ArgType
candidateType
)
{
...
...
@@ -356,7 +393,7 @@ public final class TypeUpdate {
TypeUpdateResult
result
=
updateTypeChecked
(
updateInfo
,
changeArg
,
candidateType
);
if
(
result
==
SAME
&&
!
correctType
)
{
if
(
Consts
.
DEBUG
)
{
if
(
Consts
.
DEBUG
_TYPE_INFERENCE
)
{
LOG
.
debug
(
"Move insn types mismatch: {} -> {}, change arg: {}, insn: {}"
,
candidateType
,
changeArg
.
getType
(),
changeArg
,
insn
);
}
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdateInfo.java
浏览文件 @
6192ced2
...
...
@@ -5,12 +5,15 @@ import java.util.List;
import
jadx.core.dex.instructions.args.ArgType
;
import
jadx.core.dex.instructions.args.InsnArg
;
import
jadx.core.dex.nodes.MethodNode
;
public
class
TypeUpdateInfo
{
private
final
MethodNode
mth
;
private
final
TypeUpdateFlags
flags
;
private
final
List
<
TypeUpdateEntry
>
updates
=
new
ArrayList
<>();
public
TypeUpdateInfo
(
TypeUpdateFlags
flags
)
{
public
TypeUpdateInfo
(
MethodNode
mth
,
TypeUpdateFlags
flags
)
{
this
.
mth
=
mth
;
this
.
flags
=
flags
;
}
...
...
@@ -50,6 +53,10 @@ public class TypeUpdateInfo {
updates
.
removeIf
(
updateEntry
->
updateEntry
.
getArg
()
==
arg
);
}
public
MethodNode
getMth
()
{
return
mth
;
}
public
List
<
TypeUpdateEntry
>
getUpdates
()
{
return
updates
;
}
...
...
jadx-core/src/test/java/jadx/tests/integration/invoke/TestCastInOverloadedInvoke.java
浏览文件 @
6192ced2
...
...
@@ -64,7 +64,7 @@ public class TestCastInOverloadedInvoke extends IntegrationTest {
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsOne
(
"call(
(ArrayList<String>) new ArrayList
());"
));
assertThat
(
code
,
containsOne
(
"call(
new ArrayList<>
());"
));
assertThat
(
code
,
containsOne
(
"call((List<String>) new ArrayList());"
));
assertThat
(
code
,
containsOne
(
"call((String) obj);"
));
...
...
@@ -76,9 +76,6 @@ public class TestCastInOverloadedInvoke extends IntegrationTest {
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsOne
(
"call(new ArrayList<>());"
));
assertThat
(
code
,
containsOne
(
"call((List<String>) new ArrayList<String>());"
));
assertThat
(
code
,
containsOne
(
"call((String) obj);"
));
}
}
jadx-core/src/test/java/jadx/tests/integration/invoke/TestHierarchyOverloadedInvoke.java
浏览文件 @
6192ced2
...
...
@@ -5,7 +5,6 @@ import java.util.List;
import
org.junit.jupiter.api.Test
;
import
jadx.NotYetImplemented
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.tests.api.IntegrationTest
;
...
...
@@ -83,18 +82,9 @@ public class TestHierarchyOverloadedInvoke extends IntegrationTest {
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsOne
(
"b.call(
(ArrayList<String>) new ArrayList
());"
));
assertThat
(
code
,
containsOne
(
"b.call(
new ArrayList<>
());"
));
assertThat
(
code
,
containsOne
(
"b.call((List<String>) new ArrayList());"
));
assertThat
(
code
,
containsOne
(
"b.call((String) obj);"
));
}
@NotYetImplemented
@Test
public
void
test2
()
{
ClassNode
cls
=
getClassNode
(
TestCls
.
class
);
String
code
=
cls
.
getCode
().
toString
();
assertThat
(
code
,
containsOne
(
"b.call(new ArrayList<>());"
));
}
}
jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics6.java
0 → 100644
浏览文件 @
6192ced2
package
jadx.tests.integration.types
;
import
java.util.Iterator
;
import
java.util.Map
;
import
org.junit.jupiter.api.Test
;
import
jadx.tests.api.IntegrationTest
;
import
static
jadx
.
tests
.
api
.
utils
.
assertj
.
JadxAssertions
.
assertThat
;
public
class
TestGenerics6
extends
IntegrationTest
{
public
static
class
TestCls
<
K
,
V
>
implements
Iterable
<
Map
.
Entry
<
K
,
V
>>
{
public
V
test
(
K
key
,
V
v
)
{
Entry
<
K
,
V
>
entry
=
get
(
key
);
if
(
entry
!=
null
)
{
return
entry
.
mValue
;
}
put
(
key
,
v
);
return
null
;
}
protected
Entry
<
K
,
V
>
get
(
K
k
)
{
return
null
;
}
protected
Entry
<
K
,
V
>
put
(
K
key
,
V
v
)
{
return
null
;
}
@Override
public
Iterator
<
Map
.
Entry
<
K
,
V
>>
iterator
()
{
return
null
;
}
static
class
Entry
<
K
,
V
>
implements
Map
.
Entry
<
K
,
V
>
{
final
V
mValue
;
Entry
(
K
key
,
V
value
)
{
this
.
mValue
=
value
;
}
@Override
public
K
getKey
()
{
return
null
;
}
@Override
public
V
getValue
()
{
return
null
;
}
@Override
public
V
setValue
(
V
value
)
{
return
null
;
}
}
}
@Test
public
void
test
()
{
noDebugInfo
();
assertThat
(
getClassNode
(
TestCls
.
class
))
.
code
()
.
doesNotContain
(
"Entry entry = get(k);"
)
.
containsOne
(
"Entry<K, V> entry = get(k);"
);
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录