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
7654661b
Commit
7654661b
authored
Mar 15, 2019
by
Skylot
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: inline desugared lambda classes (#467)
parent
51a9c741
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
99 additions
and
66 deletions
+99
-66
JadxDecompiler.java
jadx-core/src/main/java/jadx/api/JadxDecompiler.java
+6
-8
ProcessClass.java
jadx-core/src/main/java/jadx/core/ProcessClass.java
+5
-7
ClassGen.java
jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
+1
-1
CodeGen.java
jadx-core/src/main/java/jadx/core/codegen/CodeGen.java
+11
-6
CodeWriter.java
jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java
+5
-2
InsnGen.java
jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
+3
-10
ClassNode.java
jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java
+25
-1
ClassModifier.java
...e/src/main/java/jadx/core/dex/visitors/ClassModifier.java
+27
-20
DependencyCollector.java
...main/java/jadx/core/dex/visitors/DependencyCollector.java
+4
-0
RegionUtils.java
jadx-core/src/main/java/jadx/core/utils/RegionUtils.java
+6
-1
IntegrationTest.java
jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
+2
-2
BaseExternalTest.java
...e/src/test/java/jadx/tests/external/BaseExternalTest.java
+4
-8
No files found.
jadx-core/src/main/java/jadx/api/JadxDecompiler.java
View file @
7654661b
...
...
@@ -17,7 +17,6 @@ import org.slf4j.LoggerFactory;
import
jadx.core.Jadx
;
import
jadx.core.ProcessClass
;
import
jadx.core.codegen.CodeGen
;
import
jadx.core.dex.attributes.AFlag
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.core.dex.nodes.FieldNode
;
...
...
@@ -59,7 +58,6 @@ public final class JadxDecompiler {
private
RootNode
root
;
private
List
<
IDexTreeVisitor
>
passes
;
private
CodeGen
codeGen
;
private
List
<
JavaClass
>
classes
;
private
List
<
ResourceFile
>
resources
;
...
...
@@ -97,7 +95,6 @@ public final class JadxDecompiler {
void
init
()
{
this
.
passes
=
Jadx
.
getPassesList
(
args
);
this
.
codeGen
=
new
CodeGen
();
}
void
reset
()
{
...
...
@@ -106,7 +103,6 @@ public final class JadxDecompiler {
xmlParser
=
null
;
root
=
null
;
passes
=
null
;
codeGen
=
null
;
}
public
static
String
getVersion
()
{
...
...
@@ -215,9 +211,11 @@ public final class JadxDecompiler {
List
<
JavaClass
>
clsList
=
new
ArrayList
<>(
classNodeList
.
size
());
classesMap
.
clear
();
for
(
ClassNode
classNode
:
classNodeList
)
{
JavaClass
javaClass
=
new
JavaClass
(
classNode
,
this
);
clsList
.
add
(
javaClass
);
classesMap
.
put
(
classNode
,
javaClass
);
if
(!
classNode
.
contains
(
AFlag
.
DONT_GENERATE
))
{
JavaClass
javaClass
=
new
JavaClass
(
classNode
,
this
);
clsList
.
add
(
javaClass
);
classesMap
.
put
(
classNode
,
javaClass
);
}
}
classes
=
Collections
.
unmodifiableList
(
clsList
);
}
...
...
@@ -289,7 +287,7 @@ public final class JadxDecompiler {
}
void
processClass
(
ClassNode
cls
)
{
ProcessClass
.
process
(
cls
,
passes
,
codeGen
);
ProcessClass
.
process
(
cls
,
passes
,
true
);
}
RootNode
getRoot
()
{
...
...
jadx-core/src/main/java/jadx/core/ProcessClass.java
View file @
7654661b
...
...
@@ -2,8 +2,6 @@ package jadx.core;
import
java.util.List
;
import
org.jetbrains.annotations.Nullable
;
import
jadx.core.codegen.CodeGen
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.core.dex.visitors.DepthTraversal
;
...
...
@@ -19,8 +17,8 @@ public final class ProcessClass {
private
ProcessClass
()
{
}
public
static
void
process
(
ClassNode
cls
,
List
<
IDexTreeVisitor
>
passes
,
@Nullable
CodeGen
codeGen
)
{
if
(
codeGen
==
null
&&
cls
.
getState
()
==
PROCESSED
)
{
public
static
void
process
(
ClassNode
cls
,
List
<
IDexTreeVisitor
>
passes
,
boolean
generateCode
)
{
if
(
!
generateCode
&&
cls
.
getState
()
==
PROCESSED
)
{
return
;
}
synchronized
(
getSyncObj
(
cls
))
{
...
...
@@ -33,9 +31,9 @@ public final class ProcessClass {
}
cls
.
setState
(
PROCESSED
);
}
if
(
cls
.
getState
()
==
PROCESSED
&&
codeGen
!=
null
)
{
if
(
cls
.
getState
()
==
PROCESSED
&&
generateCode
)
{
processDependencies
(
cls
,
passes
);
codeGen
.
visit
(
cls
);
CodeGen
.
generate
(
cls
);
}
}
catch
(
Exception
e
)
{
ErrorsCounter
.
classError
(
cls
,
e
.
getClass
().
getSimpleName
(),
e
);
...
...
@@ -48,6 +46,6 @@ public final class ProcessClass {
}
private
static
void
processDependencies
(
ClassNode
cls
,
List
<
IDexTreeVisitor
>
passes
)
{
cls
.
getDependencies
().
forEach
(
depCls
->
process
(
depCls
,
passes
,
null
));
cls
.
getDependencies
().
forEach
(
depCls
->
process
(
depCls
,
passes
,
false
));
}
}
jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
View file @
7654661b
...
...
@@ -98,7 +98,7 @@ public class ClassGen {
imports
.
clear
();
}
clsCode
.
add
(
clsBody
);
return
clsCode
;
return
clsCode
.
finish
()
;
}
public
void
addClassCode
(
CodeWriter
code
)
throws
CodegenException
{
...
...
jadx-core/src/main/java/jadx/core/codegen/CodeGen.java
View file @
7654661b
package
jadx
.
core
.
codegen
;
import
jadx.core.dex.attributes.AFlag
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.core.utils.exceptions.CodegenException
;
public
class
CodeGen
{
public
boolean
visit
(
ClassNode
cls
)
throws
CodegenException
{
ClassGen
clsGen
=
new
ClassGen
(
cls
,
cls
.
root
().
getArgs
());
CodeWriter
clsCode
=
clsGen
.
makeClass
();
clsCode
.
finish
();
cls
.
setCode
(
clsCode
);
return
false
;
public
static
void
generate
(
ClassNode
cls
)
throws
CodegenException
{
if
(
cls
.
contains
(
AFlag
.
DONT_GENERATE
))
{
cls
.
setCode
(
CodeWriter
.
EMPTY
);
}
else
{
ClassGen
clsGen
=
new
ClassGen
(
cls
,
cls
.
root
().
getArgs
());
cls
.
setCode
(
clsGen
.
makeClass
());
}
}
private
CodeGen
()
{
}
}
jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java
View file @
7654661b
...
...
@@ -24,6 +24,8 @@ public class CodeWriter {
public
static
final
String
NL
=
System
.
getProperty
(
"line.separator"
);
public
static
final
String
INDENT_STR
=
" "
;
public
static
final
CodeWriter
EMPTY
=
new
CodeWriter
().
finish
();
private
static
final
boolean
ADD_LINE_NUMBERS
=
false
;
private
static
final
String
[]
INDENT_CACHE
=
{
...
...
@@ -250,7 +252,7 @@ public class CodeWriter {
return
lineMap
;
}
public
void
finish
()
{
public
CodeWriter
finish
()
{
removeFirstEmptyLine
();
buf
.
trimToSize
();
code
=
buf
.
toString
();
...
...
@@ -266,11 +268,12 @@ public class CodeWriter {
it
.
remove
();
}
}
return
this
;
}
private
void
removeFirstEmptyLine
()
{
int
len
=
NL
.
length
();
if
(
buf
.
substring
(
0
,
len
).
equals
(
NL
))
{
if
(
buf
.
length
()
>
len
&&
buf
.
substring
(
0
,
len
).
equals
(
NL
))
{
buf
.
delete
(
0
,
len
);
}
}
...
...
jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
View file @
7654661b
...
...
@@ -48,7 +48,6 @@ import jadx.core.dex.nodes.FieldNode;
import
jadx.core.dex.nodes.InsnNode
;
import
jadx.core.dex.nodes.MethodNode
;
import
jadx.core.dex.nodes.RootNode
;
import
jadx.core.utils.ErrorsCounter
;
import
jadx.core.utils.RegionUtils
;
import
jadx.core.utils.exceptions.CodegenException
;
import
jadx.core.utils.exceptions.JadxRuntimeException
;
...
...
@@ -559,7 +558,7 @@ public class InsnGen {
throws
CodegenException
{
ClassNode
cls
=
mth
.
dex
().
resolveClass
(
insn
.
getClassType
());
if
(
cls
!=
null
&&
cls
.
contains
(
AFlag
.
ANONYMOUS_CLASS
)
&&
!
fallback
)
{
inlineAnonymousConstr
(
code
,
cls
,
insn
);
inlineAnonymousConstr
uctor
(
code
,
cls
,
insn
);
return
;
}
if
(
insn
.
isSelf
())
{
...
...
@@ -577,20 +576,14 @@ public class InsnGen {
generateMethodArguments
(
code
,
insn
,
0
,
callMth
);
}
private
void
inlineAnonymousConstr
(
CodeWriter
code
,
ClassNode
cls
,
ConstructorInsn
insn
)
throws
CodegenException
{
// anonymous class construction
if
(
cls
.
contains
(
AFlag
.
DONT_GENERATE
))
{
code
.
add
(
"/* anonymous class already generated */"
);
ErrorsCounter
.
methodWarn
(
mth
,
"Anonymous class already generated: "
+
cls
);
return
;
}
private
void
inlineAnonymousConstructor
(
CodeWriter
code
,
ClassNode
cls
,
ConstructorInsn
insn
)
throws
CodegenException
{
cls
.
add
(
AFlag
.
DONT_GENERATE
);
ArgType
parent
;
if
(
cls
.
getInterfaces
().
size
()
==
1
)
{
parent
=
cls
.
getInterfaces
().
get
(
0
);
}
else
{
parent
=
cls
.
getSuperClass
();
}
cls
.
add
(
AFlag
.
DONT_GENERATE
);
MethodNode
defCtr
=
cls
.
getDefaultConstructor
();
if
(
defCtr
!=
null
)
{
if
(
RegionUtils
.
notEmpty
(
defCtr
.
getRegion
()))
{
...
...
jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java
View file @
7654661b
...
...
@@ -19,6 +19,7 @@ import org.slf4j.LoggerFactory;
import
jadx.core.Consts
;
import
jadx.core.codegen.CodeWriter
;
import
jadx.core.dex.attributes.AFlag
;
import
jadx.core.dex.attributes.annotations.Annotation
;
import
jadx.core.dex.attributes.nodes.LineAttrNode
;
import
jadx.core.dex.attributes.nodes.SourceFileAttr
;
...
...
@@ -123,7 +124,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode {
accFlagsValue
=
cls
.
getAccessFlags
();
}
this
.
accessFlags
=
new
AccessInfo
(
accFlagsValue
,
AFType
.
CLASS
);
markAnonymousClass
(
this
);
buildCache
();
}
catch
(
Exception
e
)
{
throw
new
JadxRuntimeException
(
"Error decode class: "
+
clsInfo
,
e
);
...
...
@@ -394,6 +395,29 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode {
&&
getDefaultConstructor
()
!=
null
;
}
public
boolean
isLambdaCls
()
{
return
accessFlags
.
isSynthetic
()
&&
accessFlags
.
isFinal
()
&&
clsInfo
.
getType
().
getObject
().
contains
(
".-$$Lambda$"
)
&&
countStaticFields
()
==
0
;
}
private
int
countStaticFields
()
{
int
c
=
0
;
for
(
FieldNode
field
:
fields
)
{
if
(
field
.
getAccessFlags
().
isStatic
())
{
c
++;
}
}
return
c
;
}
private
static
void
markAnonymousClass
(
ClassNode
cls
)
{
if
(
cls
.
isAnonymous
()
||
cls
.
isLambdaCls
())
{
cls
.
add
(
AFlag
.
ANONYMOUS_CLASS
);
cls
.
add
(
AFlag
.
DONT_GENERATE
);
}
}
@Nullable
public
MethodNode
getClassInitMth
()
{
return
searchMethodByName
(
"<clinit>()V"
);
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java
View file @
7654661b
...
...
@@ -54,40 +54,46 @@ public class ClassModifier extends AbstractVisitor {
cls
.
add
(
AFlag
.
DONT_GENERATE
);
return
false
;
}
markAnonymousClass
(
cls
);
removeSyntheticFields
(
cls
);
cls
.
getMethods
().
forEach
(
mth
->
removeSyntheticMethods
(
cls
,
mth
)
);
cls
.
getMethods
().
forEach
(
ClassModifier:
:
removeSyntheticMethods
);
cls
.
getMethods
().
forEach
(
ClassModifier:
:
removeEmptyMethods
);
markAnonymousClass
(
cls
);
return
false
;
}
private
void
markAnonymousClass
(
ClassNode
cls
)
{
if
(
cls
.
isAnonymous
())
{
cls
.
add
(
AFlag
.
ANONYMOUS_CLASS
);
cls
.
add
(
AFlag
.
DONT_GENERATE
);
}
}
/**
* Remove synthetic fields if type is outer class or class will be inlined (anonymous)
*/
private
static
void
removeSyntheticFields
(
ClassNode
cls
)
{
if
(
!
cls
.
getClassInfo
().
isInner
()
||
cls
.
getAccessFlags
().
isStatic
())
{
if
(
cls
.
getAccessFlags
().
isStatic
())
{
return
;
}
// remove fields if it is synthetic and type is a outer class
for
(
FieldNode
field
:
cls
.
getFields
())
{
if
(
field
.
getAccessFlags
().
isSynthetic
()
&&
field
.
getType
().
isObject
())
{
ClassInfo
clsInfo
=
ClassInfo
.
fromType
(
cls
.
root
(),
field
.
getType
());
ClassNode
fieldsCls
=
cls
.
dex
().
resolveClass
(
clsInfo
);
ClassInfo
parentClass
=
cls
.
getClassInfo
().
getParentClass
();
if
(
fieldsCls
!=
null
&&
parentClass
.
equals
(
fieldsCls
.
getClassInfo
()))
{
int
found
=
0
;
for
(
MethodNode
mth
:
cls
.
getMethods
())
{
if
(
removeFieldUsageFromConstructor
(
mth
,
field
,
fieldsCls
))
{
found
++;
boolean
inline
=
cls
.
contains
(
AFlag
.
ANONYMOUS_CLASS
);
if
(
inline
||
cls
.
getClassInfo
().
isInner
())
{
for
(
FieldNode
field
:
cls
.
getFields
())
{
if
(
field
.
getAccessFlags
().
isSynthetic
()
&&
field
.
getType
().
isObject
())
{
ClassInfo
clsInfo
=
ClassInfo
.
fromType
(
cls
.
root
(),
field
.
getType
());
ClassNode
fieldsCls
=
cls
.
dex
().
resolveClass
(
clsInfo
);
ClassInfo
parentClass
=
cls
.
getClassInfo
().
getParentClass
();
if
(
fieldsCls
!=
null
&&
(
inline
||
parentClass
.
equals
(
fieldsCls
.
getClassInfo
())))
{
int
found
=
0
;
for
(
MethodNode
mth
:
cls
.
getMethods
())
{
if
(
removeFieldUsageFromConstructor
(
mth
,
field
,
fieldsCls
))
{
found
++;
}
}
if
(
found
!=
0
)
{
field
.
addAttr
(
new
FieldReplaceAttr
(
fieldsCls
.
getClassInfo
()));
field
.
add
(
AFlag
.
DONT_GENERATE
);
}
}
if
(
found
!=
0
)
{
field
.
addAttr
(
new
FieldReplaceAttr
(
parentClass
));
field
.
add
(
AFlag
.
DONT_GENERATE
);
}
}
}
...
...
@@ -133,7 +139,7 @@ public class ClassModifier extends AbstractVisitor {
return
true
;
}
private
static
void
removeSyntheticMethods
(
ClassNode
cls
,
MethodNode
mth
)
{
private
static
void
removeSyntheticMethods
(
MethodNode
mth
)
{
if
(
mth
.
isNoCode
())
{
return
;
}
...
...
@@ -141,6 +147,7 @@ public class ClassModifier extends AbstractVisitor {
if
(!
af
.
isSynthetic
())
{
return
;
}
ClassNode
cls
=
mth
.
getParentClass
();
if
(
removeBridgeMethod
(
cls
,
mth
))
{
if
(
Consts
.
DEBUG
)
{
mth
.
addAttr
(
AType
.
COMMENTS
,
"Removed as synthetic bridge method"
);
...
...
jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java
View file @
7654661b
...
...
@@ -11,6 +11,7 @@ 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.RegisterArg
;
import
jadx.core.dex.instructions.mods.ConstructorInsn
;
import
jadx.core.dex.nodes.BlockNode
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.core.dex.nodes.DexNode
;
...
...
@@ -90,6 +91,9 @@ public class DependencyCollector extends AbstractVisitor {
}
else
if
(
insn
instanceof
InvokeNode
)
{
ClassInfo
declClass
=
((
InvokeNode
)
insn
).
getCallMth
().
getDeclClass
();
addDep
(
dex
,
depList
,
declClass
);
}
else
if
(
insn
instanceof
ConstructorInsn
)
{
ClassInfo
declClass
=
((
ConstructorInsn
)
insn
).
getCallMth
().
getDeclClass
();
addDep
(
dex
,
depList
,
declClass
);
}
}
...
...
jadx-core/src/main/java/jadx/core/utils/RegionUtils.java
View file @
7654661b
...
...
@@ -5,6 +5,8 @@ import java.util.Collections;
import
java.util.List
;
import
java.util.Set
;
import
org.jetbrains.annotations.Nullable
;
import
jadx.core.dex.attributes.AType
;
import
jadx.core.dex.instructions.InsnType
;
import
jadx.core.dex.nodes.BlockNode
;
...
...
@@ -136,7 +138,10 @@ public class RegionUtils {
return
!
notEmpty
(
container
);
}
public
static
boolean
notEmpty
(
IContainer
container
)
{
public
static
boolean
notEmpty
(
@Nullable
IContainer
container
)
{
if
(
container
==
null
)
{
return
false
;
}
if
(
container
instanceof
IBlock
)
{
return
!((
IBlock
)
container
).
getInstructions
().
isEmpty
();
}
else
if
(
container
instanceof
IRegion
)
{
...
...
jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
View file @
7654661b
...
...
@@ -139,7 +139,7 @@ public abstract class IntegrationTest extends TestUtils {
protected
void
decompile
(
JadxDecompiler
jadx
,
ClassNode
cls
)
{
List
<
IDexTreeVisitor
>
passes
=
getPassesList
(
jadx
);
ProcessClass
.
process
(
cls
,
passes
,
new
CodeGen
()
);
ProcessClass
.
process
(
cls
,
passes
,
true
);
}
protected
void
decompileWithoutUnload
(
JadxDecompiler
jadx
,
ClassNode
cls
)
{
...
...
@@ -168,7 +168,7 @@ public abstract class IntegrationTest extends TestUtils {
protected
void
generateClsCode
(
ClassNode
cls
)
{
try
{
new
CodeGen
().
visit
(
cls
);
CodeGen
.
generate
(
cls
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
fail
(
e
.
getMessage
());
...
...
jadx-core/src/test/java/jadx/tests/external/BaseExternalTest.java
View file @
7654661b
...
...
@@ -15,12 +15,11 @@ import jadx.api.JadxDecompiler;
import
jadx.api.JadxInternalAccess
;
import
jadx.api.JavaClass
;
import
jadx.core.Jadx
;
import
jadx.core.
codegen.CodeGen
;
import
jadx.core.
ProcessClass
;
import
jadx.core.codegen.CodeWriter
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.core.dex.nodes.MethodNode
;
import
jadx.core.dex.nodes.RootNode
;
import
jadx.core.dex.visitors.DepthTraversal
;
import
jadx.core.dex.visitors.IDexTreeVisitor
;
import
jadx.core.utils.exceptions.JadxRuntimeException
;
import
jadx.tests.api.IntegrationTest
;
...
...
@@ -77,7 +76,7 @@ public abstract class BaseExternalTest extends IntegrationTest {
for
(
ClassNode
classNode
:
root
.
getClasses
(
true
))
{
String
clsFullName
=
classNode
.
getClassInfo
().
getFullName
();
if
(
clsPattern
.
matcher
(
clsFullName
).
matches
())
{
if
(
processCls
(
mthPattern
,
passes
,
classNode
))
{
if
(
processCls
(
jadx
,
mthPattern
,
passes
,
classNode
))
{
processed
++;
}
}
...
...
@@ -85,7 +84,7 @@ public abstract class BaseExternalTest extends IntegrationTest {
assertThat
(
"No classes processed"
,
processed
,
greaterThan
(
0
));
}
private
boolean
processCls
(
@Nullable
Pattern
mthPattern
,
List
<
IDexTreeVisitor
>
passes
,
ClassNode
classNode
)
{
private
boolean
processCls
(
JadxDecompiler
jadx
,
@Nullable
Pattern
mthPattern
,
List
<
IDexTreeVisitor
>
passes
,
ClassNode
classNode
)
{
classNode
.
load
();
boolean
decompile
=
false
;
if
(
mthPattern
==
null
)
{
...
...
@@ -101,11 +100,8 @@ public abstract class BaseExternalTest extends IntegrationTest {
if
(!
decompile
)
{
return
false
;
}
for
(
IDexTreeVisitor
visitor
:
passes
)
{
DepthTraversal
.
visit
(
visitor
,
classNode
);
}
try
{
new
CodeGen
().
visit
(
classNod
e
);
ProcessClass
.
process
(
classNode
,
passes
,
tru
e
);
}
catch
(
Exception
e
)
{
throw
new
JadxRuntimeException
(
"Codegen failed"
,
e
);
}
...
...
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