Merge remote-tracking branch 'upstream/master' into runelite
# Conflicts: # build.xml # src/org/jetbrains/java/decompiler/main/ClassWriter.java # src/org/jetbrains/java/decompiler/main/InitializerProcessor.java # src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java # src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java # src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java # src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java # src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java # src/org/jetbrains/java/decompiler/util/TextUtil.java
This commit is contained in:
6
.editorconfig
Normal file
6
.editorconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
[*]
|
||||
charset=utf-8
|
||||
end_of_line=lf
|
||||
insert_final_newline=false
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/.idea/
|
||||
/.gradle/
|
||||
/build/
|
||||
84
README.md
Normal file
84
README.md
Normal file
@@ -0,0 +1,84 @@
|
||||
### About Fernflower
|
||||
|
||||
Fernflower is the first actually working analytical decompiler for Java and
|
||||
probably for a high-level programming language in general. Naturally it is still
|
||||
under development, please send your bug reports and improvement suggestions to the
|
||||
[issue tracker](https://youtrack.jetbrains.com/newIssue?project=IDEA&clearDraft=true&c=Subsystem+Decompiler).
|
||||
|
||||
### Licence
|
||||
|
||||
Fernflower is licenced under the [Apache Licence Version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
||||
### Running from command line
|
||||
|
||||
`java -jar fernflower.jar [-<option>=<value>]* [<source>]+ <destination>`
|
||||
|
||||
\* means 0 or more times\
|
||||
\+ means 1 or more times
|
||||
|
||||
\<source>: file or directory with files to be decompiled. Directories are recursively scanned. Allowed file extensions are class, zip and jar.
|
||||
Sources prefixed with -e= mean "library" files that won't be decompiled, but taken into account when analysing relationships between
|
||||
classes or methods. Especially renaming of identifiers (s. option 'ren') can benefit from information about external classes.
|
||||
|
||||
\<destination>: destination directory
|
||||
|
||||
\<option>, \<value>: a command-line option with the corresponding value (see "Command-line options" below).
|
||||
|
||||
##### Examples:
|
||||
|
||||
`java -jar fernflower.jar -hes=0 -hdc=0 c:\Temp\binary\ -e=c:\Java\rt.jar c:\Temp\source\`
|
||||
|
||||
`java -jar fernflower.jar -dgs=1 c:\Temp\binary\library.jar c:\Temp\binary\Boot.class c:\Temp\source\`
|
||||
|
||||
### Command-line options
|
||||
|
||||
With the exception of mpm and urc the value of 1 means the option is activated, 0 - deactivated. Default
|
||||
value, if any, is given between parentheses.
|
||||
|
||||
Typically, the following options will be changed by user, if any: hes, hdc, dgs, mpm, ren, urc
|
||||
The rest of options can be left as they are: they are aimed at professional reverse engineers.
|
||||
|
||||
- rbr (1): hide bridge methods
|
||||
- rsy (0): hide synthetic class members
|
||||
- din (1): decompile inner classes
|
||||
- dc4 (1): collapse 1.4 class references
|
||||
- das (1): decompile assertions
|
||||
- hes (1): hide empty super invocation
|
||||
- hdc (1): hide empty default constructor
|
||||
- dgs (0): decompile generic signatures
|
||||
- ner (1): assume return not throwing exceptions
|
||||
- den (1): decompile enumerations
|
||||
- rgn (1): remove getClass() invocation, when it is part of a qualified new statement
|
||||
- lit (0): output numeric literals "as-is"
|
||||
- asc (0): encode non-ASCII characters in string and character literals as Unicode escapes
|
||||
- bto (1): interpret int 1 as boolean true (workaround to a compiler bug)
|
||||
- nns (0): allow for not set synthetic attribute (workaround to a compiler bug)
|
||||
- uto (1): consider nameless types as java.lang.Object (workaround to a compiler architecture flaw)
|
||||
- udv (1): reconstruct variable names from debug information, if present
|
||||
- rer (1): remove empty exception ranges
|
||||
- fdi (1): de-inline finally structures
|
||||
- mpm (0): maximum allowed processing time per decompiled method, in seconds. 0 means no upper limit
|
||||
- ren (0): rename ambiguous (resp. obfuscated) classes and class elements
|
||||
- urc (-): full name of a user-supplied class implementing IIdentifierRenamer interface. It is used to determine which class identifiers
|
||||
should be renamed and provides new identifier names (see "Renaming identifiers")
|
||||
- inn (1): check for IntelliJ IDEA-specific @NotNull annotation and remove inserted code if found
|
||||
- lac (0): decompile lambda expressions to anonymous classes
|
||||
- nls (0): define new line character to be used for output. 0 - '\r\n' (Windows), 1 - '\n' (Unix), default is OS-dependent
|
||||
- ind: indentation string (default is 3 spaces)
|
||||
- log (INFO): a logging level, possible values are TRACE, INFO, WARN, ERROR
|
||||
|
||||
### Renaming identifiers
|
||||
|
||||
Some obfuscators give classes and their member elements short, meaningless and above all ambiguous names. Recompiling of such
|
||||
code leads to a great number of conflicts. Therefore it is advisable to let the decompiler rename elements in its turn,
|
||||
ensuring uniqueness of each identifier.
|
||||
|
||||
Option 'ren' (i.e. -ren=1) activates renaming functionality. Default renaming strategy goes as follows:
|
||||
- rename an element if its name is a reserved word or is shorter than 3 characters
|
||||
- new names are built according to a simple pattern: (class|method|field)_\<consecutive unique number>
|
||||
You can overwrite this rules by providing your own implementation of the 4 key methods invoked by the decompiler while renaming. Simply
|
||||
pass a class that implements org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer in the option 'urc'
|
||||
(e.g. -urc=com.example.MyRenamer) to Fernflower. The class must be available on the application classpath.
|
||||
|
||||
The meaning of each method should be clear from naming: toBeRenamed determine whether the element will be renamed, while the other three
|
||||
provide new names for classes, methods and fields respectively.
|
||||
24
build.gradle
Normal file
24
build.gradle
Normal file
@@ -0,0 +1,24 @@
|
||||
apply plugin: 'java'
|
||||
|
||||
compileJava {
|
||||
sourceCompatibility '1.8'
|
||||
targetCompatibility '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs 'src'
|
||||
test.java.srcDirs 'test'
|
||||
}
|
||||
|
||||
repositories { jcenter() }
|
||||
dependencies {
|
||||
testCompile 'junit:junit:4.+'
|
||||
testCompile 'org.assertj:assertj-core:3.+'
|
||||
}
|
||||
|
||||
jar {
|
||||
archiveName 'fernflower.jar'
|
||||
manifest {
|
||||
attributes 'Main-Class': 'org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler'
|
||||
}
|
||||
}
|
||||
59
build.xml
59
build.xml
@@ -1,59 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="Fernflower" default="dist" basedir=".">
|
||||
|
||||
<target name="init">
|
||||
<property name="src" value="${basedir}/src"/>
|
||||
<property name="out" value="${basedir}/out/production"/>
|
||||
<property name="dist" value="${basedir}/fernflower.jar"/>
|
||||
<property name="test-src" value="${basedir}/test"/>
|
||||
<property name="test-out" value="${basedir}/out/test"/>
|
||||
</target>
|
||||
|
||||
<!-- external dependencies, adjust to your own -->
|
||||
<path id="junit">
|
||||
<file name="${basedir}/../../../lib/junit-4.12.jar"/>
|
||||
<file name="${basedir}/../../../lib/hamcrest-core-1.3.jar"/>
|
||||
<file name="${basedir}/../../../lib/hamcrest-library-1.3.jar"/>
|
||||
</path>
|
||||
|
||||
<target name="clean" depends="init">
|
||||
<delete includeemptydirs="true" failonerror="false">
|
||||
<fileset dir="${out}"/>
|
||||
<fileset dir="${test-out}"/>
|
||||
<fileset file="${dist}"/>
|
||||
</delete>
|
||||
</target>
|
||||
|
||||
<target name="compile" depends="init,clean">
|
||||
<mkdir dir="${out}"/>
|
||||
<javac srcdir="${src}" destdir="${out}" source="1.7" target="1.7" encoding="UTF-8" debug="true" includeantruntime="false"/>
|
||||
</target>
|
||||
|
||||
<target name="dist" depends="init,compile">
|
||||
<jar jarfile="${dist}" compress="true" basedir="${out}" includes="**/*.class">
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler"/>
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="test-compile" depends="init,compile">
|
||||
<mkdir dir="${test-out}"/>
|
||||
<javac srcdir="${test-src}" destdir="${test-out}" source="1.7" target="1.7" encoding="UTF-8" debug="true" includeantruntime="false">
|
||||
<classpath path="${out}"/>
|
||||
<classpath refid="junit"/>
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="test" depends="init,test-compile">
|
||||
<junit printsummary="true" failureproperty="tests.failed">
|
||||
<classpath path="${test-out}:${out}"/>
|
||||
<classpath refid="junit"/>
|
||||
<batchtest>
|
||||
<fileset dir="${test-src}" includes="**/*Test.java"/>
|
||||
</batchtest>
|
||||
</junit>
|
||||
<fail if="tests.failed" message="Tests failed."/>
|
||||
</target>
|
||||
|
||||
</project>
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Tue Nov 28 14:11:56 CET 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip
|
||||
172
gradlew
vendored
Executable file
172
gradlew
vendored
Executable file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
84
gradlew.bat
vendored
Normal file
84
gradlew.bat
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -9,5 +9,6 @@
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="assertJ" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
88
readme.txt
88
readme.txt
@@ -1,88 +0,0 @@
|
||||
1. About the decompiler
|
||||
|
||||
Fernflower is the first actually working analytical decompiler for Java and
|
||||
probably for a high-level programming language in general. Naturally it is still
|
||||
under development, please send your bug reports and improvement suggestions at
|
||||
fernflower.decompiler@gmail.com
|
||||
|
||||
|
||||
2. License
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
|
||||
3. Running from the command line
|
||||
|
||||
java -jar fernflower.jar [-<option>=<value>]* [<source>]+ <destination>
|
||||
|
||||
* means 0 or more times
|
||||
+ means 1 or more times
|
||||
|
||||
<source>: file or directory with files to be decompiled. Directories are recursively scanned. Allowed file extensions are class, zip and jar.
|
||||
Sources prefixed with -e= mean "library" files that won't be decompiled, but taken into account when analysing relationships between
|
||||
classes or methods. Especially renaming of identifiers (s. option 'ren') can benefit from information about external classes.
|
||||
<destination>: destination directory
|
||||
<option>,<value>: command line option with the corresponding value, see 4.
|
||||
|
||||
Examples:
|
||||
|
||||
java -jar fernflower.jar -hes=0 -hdc=0 c:\Temp\binary\ -e=c:\Java\rt.jar c:\Temp\source\
|
||||
|
||||
java -jar fernflower.jar -dgs=1 c:\Temp\binary\library.jar c:\Temp\binary\Boot.class c:\Temp\source\
|
||||
|
||||
|
||||
4. Command line options
|
||||
|
||||
With the exception of mpm and urc the value of 1 means the option is activated, 0 - deactivated. Default
|
||||
value, if any, is given between parentheses.
|
||||
|
||||
Typically, the following options will be changed by user, if any: hes, hdc, dgs, mpm, ren, urc
|
||||
The rest of options can be left as they are: they are aimed at professional reverse engineers.
|
||||
|
||||
rbr (1): hide bridge methods
|
||||
rsy (0): hide synthetic class members
|
||||
din (1): decompile inner classes
|
||||
dc4 (1): collapse 1.4 class references
|
||||
das (1): decompile assertions
|
||||
hes (1): hide empty super invocation
|
||||
hdc (1): hide empty default constructor
|
||||
dgs (0): decompile generic signatures
|
||||
ner (1): assume return not throwing exceptions
|
||||
den (1): decompile enumerations
|
||||
rgn (1): remove getClass() invocation, when it is part of a qualified new statement
|
||||
lit (0): output numeric literals "as-is"
|
||||
asc (0): encode non-ASCII characters in string and character literals as Unicode escapes
|
||||
bto (1): interpret int 1 as boolean true (workaround to a compiler bug)
|
||||
nns (1): allow for not set synthetic attribute (workaround to a compiler bug)
|
||||
uto (1): consider nameless types as java.lang.Object (workaround to a compiler architecture flaw)
|
||||
udv (1): reconstruct variable names from debug information, if present
|
||||
rer (1): remove empty exception ranges
|
||||
fdi (1): de-inline finally structures
|
||||
mpm (0): maximum allowed processing time per decompiled method, in seconds. 0 means no upper limit
|
||||
ren (0): rename ambiguous (resp. obfuscated) classes and class elements
|
||||
urc : full name of user-supplied class implementing IIdentifierRenamer. It is used to determine which class identifiers
|
||||
should be renamed and provides new identifier names. For more information see section 5
|
||||
inn (1): check for IntelliJ IDEA-specific @NotNull annotation and remove inserted code if found
|
||||
lac (0): decompile lambda expressions to anonymous classes
|
||||
nls (0): define new line character to be used for output. 0 - '\r\n' (Windows), 1 - '\n' (Unix), default is OS-dependent
|
||||
ind : indentation string (default is " " (3 spaces))
|
||||
|
||||
The default logging level is INFO. This value can be overwritten by setting the option 'log' as follows:
|
||||
log (INFO): possible values TRACE, INFO, WARN, ERROR
|
||||
|
||||
|
||||
5. Renaming identifiers
|
||||
|
||||
Some obfuscators give classes and their member elements short, meaningless and above all ambiguous names. Recompiling of such
|
||||
code leads to a great number of conflicts. Therefore it is advisable to let the decompiler rename elements in its turn,
|
||||
ensuring uniqueness of each identifier.
|
||||
|
||||
Option 'ren' (i.e. -ren=1) activates renaming functionality. Default renaming strategy goes as follows:
|
||||
- rename an element if its name is a reserved word or is shorter than 3 characters
|
||||
- new names are built according to a simple pattern: (class|method|field)_<consecutive unique number>
|
||||
You can overwrite this rules by providing your own implementation of the 4 key methods invoked by the decompiler while renaming. Simply
|
||||
pass a class that implements org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer in the option 'urc'
|
||||
(e.g. -urc=com.mypackage.MyRenamer) to Fernflower. The class must be available on the application classpath.
|
||||
|
||||
The meaning of each method should be clear from naming: toBeRenamed determine whether the element will be renamed, while the other three
|
||||
provide new names for classes, methods and fields respectively.
|
||||
@@ -1,31 +1,22 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.code;
|
||||
|
||||
@SuppressWarnings({"unused", "SpellCheckingInspection"})
|
||||
public interface CodeConstants {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// BYTECODE VERSIONS
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int BYTECODE_JAVA_LE_4 = 1;
|
||||
int BYTECODE_JAVA_5 = 2;
|
||||
int BYTECODE_JAVA_6 = 3;
|
||||
int BYTECODE_JAVA_7 = 4;
|
||||
int BYTECODE_JAVA_8 = 5;
|
||||
int BYTECODE_JAVA_LE_4 = 48;
|
||||
int BYTECODE_JAVA_5 = 49;
|
||||
int BYTECODE_JAVA_6 = 50;
|
||||
int BYTECODE_JAVA_7 = 51;
|
||||
int BYTECODE_JAVA_8 = 52;
|
||||
int BYTECODE_JAVA_9 = 53;
|
||||
int BYTECODE_JAVA_10 = 54;
|
||||
int BYTECODE_JAVA_11 = 55;
|
||||
int BYTECODE_JAVA_12 = 56;
|
||||
int BYTECODE_JAVA_13 = 57;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// VARIABLE TYPES
|
||||
@@ -63,20 +54,6 @@ public interface CodeConstants {
|
||||
int TYPE_FAMILY_DOUBLE = 5;
|
||||
int TYPE_FAMILY_OBJECT = 6;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// MODULE CONSTANTS
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int STACKSIZE_SIMPLE = 1;
|
||||
int STACKSIZE_DOUBLE = 2;
|
||||
|
||||
int VAR_LOCAL = 0;
|
||||
int VAR_STACK = 1;
|
||||
|
||||
int VAR_WRITE = 0;
|
||||
int VAR_READ = 1;
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// ACCESS FLAGS
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -97,6 +74,7 @@ public interface CodeConstants {
|
||||
int ACC_SYNTHETIC = 0x1000;
|
||||
int ACC_ANNOTATION = 0x2000;
|
||||
int ACC_ENUM = 0x4000;
|
||||
int ACC_MANDATED = 0x8000;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// CLASS FLAGS
|
||||
@@ -105,17 +83,6 @@ public interface CodeConstants {
|
||||
int ACC_SUPER = 0x0020;
|
||||
int ACC_INTERFACE = 0x0200;
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// DEPENDENCY CONSTANTS
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
int DEP_CONSTANT = 0;
|
||||
int DEP_UNKNOWN = 1;
|
||||
int DEP_GENERAL = 2;
|
||||
int DEP_PARAMS = 4;
|
||||
int DEP_STATIC = 8;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// INSTRUCTION GROUPS
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -351,7 +318,6 @@ public interface CodeConstants {
|
||||
int opc_invokestatic = 184;
|
||||
int opc_invokeinterface = 185;
|
||||
int opc_invokedynamic = 186;
|
||||
int opc_xxxunusedxxx = 186;
|
||||
int opc_new = 187;
|
||||
int opc_newarray = 188;
|
||||
int opc_anewarray = 189;
|
||||
@@ -368,7 +334,6 @@ public interface CodeConstants {
|
||||
int opc_goto_w = 200;
|
||||
int opc_jsr_w = 201;
|
||||
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
String CLINIT_NAME = "<clinit>";
|
||||
String INIT_NAME = "<init>";
|
||||
}
|
||||
}
|
||||
@@ -1,482 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.optinstructions.*;
|
||||
|
||||
public class ConstantsUtil {
|
||||
|
||||
public static String getName(int opcode) {
|
||||
return opcodeNames[opcode];
|
||||
}
|
||||
|
||||
public static Instruction getInstructionInstance(int opcode, boolean wide, int group, int bytecode_version, int[] operands) {
|
||||
|
||||
Instruction instr = getInstructionInstance(opcode, bytecode_version);
|
||||
instr.wide = wide;
|
||||
instr.group = group;
|
||||
instr.bytecode_version = bytecode_version;
|
||||
instr.setOperands(operands);
|
||||
|
||||
return instr;
|
||||
}
|
||||
|
||||
private static Instruction getInstructionInstance(int opcode, int bytecode_version) {
|
||||
try {
|
||||
Instruction instr;
|
||||
|
||||
if ((opcode >= CodeConstants.opc_ifeq &&
|
||||
opcode <= CodeConstants.opc_if_acmpne) ||
|
||||
opcode == CodeConstants.opc_ifnull ||
|
||||
opcode == CodeConstants.opc_ifnonnull) {
|
||||
instr = new IfInstruction();
|
||||
}
|
||||
else {
|
||||
|
||||
Class cl = opcodeClasses[opcode];
|
||||
|
||||
if (opcode == CodeConstants.opc_invokedynamic && bytecode_version < CodeConstants.BYTECODE_JAVA_7) {
|
||||
cl = null; // instruction unused in Java 6 and before
|
||||
}
|
||||
|
||||
if (cl == null) {
|
||||
instr = new Instruction();
|
||||
}
|
||||
else {
|
||||
instr = (Instruction)cl.newInstance();
|
||||
}
|
||||
}
|
||||
|
||||
instr.opcode = opcode;
|
||||
return instr;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final String[] opcodeNames = {
|
||||
"nop", // "nop",
|
||||
"aconst_null", // "aconst_null",
|
||||
"iconst_m1", // "iconst_m1",
|
||||
"iconst_0", // "iconst_0",
|
||||
"iconst_1", // "iconst_1",
|
||||
"iconst_2", // "iconst_2",
|
||||
"iconst_3", // "iconst_3",
|
||||
"iconst_4", // "iconst_4",
|
||||
"iconst_5", // "iconst_5",
|
||||
"lconst_0", // "lconst_0",
|
||||
"lconst_1", // "lconst_1",
|
||||
"fconst_0", // "fconst_0",
|
||||
"fconst_1", // "fconst_1",
|
||||
"fconst_2", // "fconst_2",
|
||||
"dconst_0", // "dconst_0",
|
||||
"dconst_1", // "dconst_1",
|
||||
"bipush", // "bipush",
|
||||
"sipush", // "sipush",
|
||||
"ldc", // "ldc",
|
||||
"ldc_w", // "ldc_w",
|
||||
"ldc2_w", // "ldc2_w",
|
||||
"iload", // "iload",
|
||||
"lload", // "lload",
|
||||
"fload", // "fload",
|
||||
"dload", // "dload",
|
||||
"aload", // "aload",
|
||||
"iload_0", // "iload_0",
|
||||
"iload_1", // "iload_1",
|
||||
"iload_2", // "iload_2",
|
||||
"iload_3", // "iload_3",
|
||||
"lload_0", // "lload_0",
|
||||
"lload_1", // "lload_1",
|
||||
"lload_2", // "lload_2",
|
||||
"lload_3", // "lload_3",
|
||||
"fload_0", // "fload_0",
|
||||
"fload_1", // "fload_1",
|
||||
"fload_2", // "fload_2",
|
||||
"fload_3", // "fload_3",
|
||||
"dload_0", // "dload_0",
|
||||
"dload_1", // "dload_1",
|
||||
"dload_2", // "dload_2",
|
||||
"dload_3", // "dload_3",
|
||||
"aload_0", // "aload_0",
|
||||
"aload_1", // "aload_1",
|
||||
"aload_2", // "aload_2",
|
||||
"aload_3", // "aload_3",
|
||||
"iaload", // "iaload",
|
||||
"laload", // "laload",
|
||||
"faload", // "faload",
|
||||
"daload", // "daload",
|
||||
"aaload", // "aaload",
|
||||
"baload", // "baload",
|
||||
"caload", // "caload",
|
||||
"saload", // "saload",
|
||||
"istore", // "istore",
|
||||
"lstore", // "lstore",
|
||||
"fstore", // "fstore",
|
||||
"dstore", // "dstore",
|
||||
"astore", // "astore",
|
||||
"istore_0", // "istore_0",
|
||||
"istore_1", // "istore_1",
|
||||
"istore_2", // "istore_2",
|
||||
"istore_3", // "istore_3",
|
||||
"lstore_0", // "lstore_0",
|
||||
"lstore_1", // "lstore_1",
|
||||
"lstore_2", // "lstore_2",
|
||||
"lstore_3", // "lstore_3",
|
||||
"fstore_0", // "fstore_0",
|
||||
"fstore_1", // "fstore_1",
|
||||
"fstore_2", // "fstore_2",
|
||||
"fstore_3", // "fstore_3",
|
||||
"dstore_0", // "dstore_0",
|
||||
"dstore_1", // "dstore_1",
|
||||
"dstore_2", // "dstore_2",
|
||||
"dstore_3", // "dstore_3",
|
||||
"astore_0", // "astore_0",
|
||||
"astore_1", // "astore_1",
|
||||
"astore_2", // "astore_2",
|
||||
"astore_3", // "astore_3",
|
||||
"iastore", // "iastore",
|
||||
"lastore", // "lastore",
|
||||
"fastore", // "fastore",
|
||||
"dastore", // "dastore",
|
||||
"aastore", // "aastore",
|
||||
"bastore", // "bastore",
|
||||
"castore", // "castore",
|
||||
"sastore", // "sastore",
|
||||
"pop", // "pop",
|
||||
"pop2", // "pop2",
|
||||
"dup", // "dup",
|
||||
"dup_x1", // "dup_x1",
|
||||
"dup_x2", // "dup_x2",
|
||||
"dup2", // "dup2",
|
||||
"dup2_x1", // "dup2_x1",
|
||||
"dup2_x2", // "dup2_x2",
|
||||
"swap", // "swap",
|
||||
"iadd", // "iadd",
|
||||
"ladd", // "ladd",
|
||||
"fadd", // "fadd",
|
||||
"dadd", // "dadd",
|
||||
"isub", // "isub",
|
||||
"lsub", // "lsub",
|
||||
"fsub", // "fsub",
|
||||
"dsub", // "dsub",
|
||||
"imul", // "imul",
|
||||
"lmul", // "lmul",
|
||||
"fmul", // "fmul",
|
||||
"dmul", // "dmul",
|
||||
"idiv", // "idiv",
|
||||
"ldiv", // "ldiv",
|
||||
"fdiv", // "fdiv",
|
||||
"ddiv", // "ddiv",
|
||||
"irem", // "irem",
|
||||
"lrem", // "lrem",
|
||||
"frem", // "frem",
|
||||
"drem", // "drem",
|
||||
"ineg", // "ineg",
|
||||
"lneg", // "lneg",
|
||||
"fneg", // "fneg",
|
||||
"dneg", // "dneg",
|
||||
"ishl", // "ishl",
|
||||
"lshl", // "lshl",
|
||||
"ishr", // "ishr",
|
||||
"lshr", // "lshr",
|
||||
"iushr", // "iushr",
|
||||
"lushr", // "lushr",
|
||||
"iand", // "iand",
|
||||
"land", // "land",
|
||||
"ior", // "ior",
|
||||
"lor", // "lor",
|
||||
"ixor", // "ixor",
|
||||
"lxor", // "lxor",
|
||||
"iinc", // "iinc",
|
||||
"i2l", // "i2l",
|
||||
"i2f", // "i2f",
|
||||
"i2d", // "i2d",
|
||||
"l2i", // "l2i",
|
||||
"l2f", // "l2f",
|
||||
"l2d", // "l2d",
|
||||
"f2i", // "f2i",
|
||||
"f2l", // "f2l",
|
||||
"f2d", // "f2d",
|
||||
"d2i", // "d2i",
|
||||
"d2l", // "d2l",
|
||||
"d2f", // "d2f",
|
||||
"i2b", // "i2b",
|
||||
"i2c", // "i2c",
|
||||
"i2s", // "i2s",
|
||||
"lcmp", // "lcmp",
|
||||
"fcmpl", // "fcmpl",
|
||||
"fcmpg", // "fcmpg",
|
||||
"dcmpl", // "dcmpl",
|
||||
"dcmpg", // "dcmpg",
|
||||
"ifeq", // "ifeq",
|
||||
"ifne", // "ifne",
|
||||
"iflt", // "iflt",
|
||||
"ifge", // "ifge",
|
||||
"ifgt", // "ifgt",
|
||||
"ifle", // "ifle",
|
||||
"if_icmpeq", // "if_icmpeq",
|
||||
"if_icmpne", // "if_icmpne",
|
||||
"if_icmplt", // "if_icmplt",
|
||||
"if_icmpge", // "if_icmpge",
|
||||
"if_icmpgt", // "if_icmpgt",
|
||||
"if_icmple", // "if_icmple",
|
||||
"if_acmpeq", // "if_acmpeq",
|
||||
"if_acmpne", // "if_acmpne",
|
||||
"goto", // "goto",
|
||||
"jsr", // "jsr",
|
||||
"ret", // "ret",
|
||||
"tableswitch", // "tableswitch",
|
||||
"lookupswitch", // "lookupswitch",
|
||||
"ireturn", // "ireturn",
|
||||
"lreturn", // "lreturn",
|
||||
"freturn", // "freturn",
|
||||
"dreturn", // "dreturn",
|
||||
"areturn", // "areturn",
|
||||
"return", // "return",
|
||||
"getstatic", // "getstatic",
|
||||
"putstatic", // "putstatic",
|
||||
"getfield", // "getfield",
|
||||
"putfield", // "putfield",
|
||||
"invokevirtual", // "invokevirtual",
|
||||
"invokespecial", // "invokespecial",
|
||||
"invokestatic", // "invokestatic",
|
||||
"invokeinterface", // "invokeinterface",
|
||||
//"xxxunusedxxx", // "xxxunusedxxx", Java 6 and before
|
||||
"invokedynamic", // "invokedynamic", Java 7 and later
|
||||
"new", // "new",
|
||||
"newarray", // "newarray",
|
||||
"anewarray", // "anewarray",
|
||||
"arraylength", // "arraylength",
|
||||
"athrow", // "athrow",
|
||||
"checkcast", // "checkcast",
|
||||
"instanceof", // "instanceof",
|
||||
"monitorenter", // "monitorenter",
|
||||
"monitorexit", // "monitorexit",
|
||||
"wide", // "wide",
|
||||
"multianewarray", // "multianewarray",
|
||||
"ifnull", // "ifnull",
|
||||
"ifnonnull", // "ifnonnull",
|
||||
"goto_w", // "goto_w",
|
||||
"jsr_w" // "jsr_w"
|
||||
};
|
||||
|
||||
private static final Class[] opcodeClasses = {
|
||||
null, // "nop",
|
||||
null, // "aconst_null",
|
||||
null, // "iconst_m1",
|
||||
null, // "iconst_0",
|
||||
null, // "iconst_1",
|
||||
null, // "iconst_2",
|
||||
null, // "iconst_3",
|
||||
null, // "iconst_4",
|
||||
null, // "iconst_5",
|
||||
null, // "lconst_0",
|
||||
null, // "lconst_1",
|
||||
null, // "fconst_0",
|
||||
null, // "fconst_1",
|
||||
null, // "fconst_2",
|
||||
null, // "dconst_0",
|
||||
null, // "dconst_1",
|
||||
BIPUSH.class, // "bipush",
|
||||
SIPUSH.class, // "sipush",
|
||||
LDC.class, // "ldc",
|
||||
LDC_W.class, // "ldc_w",
|
||||
LDC2_W.class, // "ldc2_w",
|
||||
ILOAD.class, // "iload",
|
||||
LLOAD.class, // "lload",
|
||||
FLOAD.class, // "fload",
|
||||
DLOAD.class, // "dload",
|
||||
ALOAD.class, // "aload",
|
||||
null, // "iload_0",
|
||||
null, // "iload_1",
|
||||
null, // "iload_2",
|
||||
null, // "iload_3",
|
||||
null, // "lload_0",
|
||||
null, // "lload_1",
|
||||
null, // "lload_2",
|
||||
null, // "lload_3",
|
||||
null, // "fload_0",
|
||||
null, // "fload_1",
|
||||
null, // "fload_2",
|
||||
null, // "fload_3",
|
||||
null, // "dload_0",
|
||||
null, // "dload_1",
|
||||
null, // "dload_2",
|
||||
null, // "dload_3",
|
||||
null, // "aload_0",
|
||||
null, // "aload_1",
|
||||
null, // "aload_2",
|
||||
null, // "aload_3",
|
||||
null, // "iaload",
|
||||
null, // "laload",
|
||||
null, // "faload",
|
||||
null, // "daload",
|
||||
null, // "aaload",
|
||||
null, // "baload",
|
||||
null, // "caload",
|
||||
null, // "saload",
|
||||
ISTORE.class, // "istore",
|
||||
LSTORE.class, // "lstore",
|
||||
FSTORE.class, // "fstore",
|
||||
DSTORE.class, // "dstore",
|
||||
ASTORE.class, // "astore",
|
||||
null, // "istore_0",
|
||||
null, // "istore_1",
|
||||
null, // "istore_2",
|
||||
null, // "istore_3",
|
||||
null, // "lstore_0",
|
||||
null, // "lstore_1",
|
||||
null, // "lstore_2",
|
||||
null, // "lstore_3",
|
||||
null, // "fstore_0",
|
||||
null, // "fstore_1",
|
||||
null, // "fstore_2",
|
||||
null, // "fstore_3",
|
||||
null, // "dstore_0",
|
||||
null, // "dstore_1",
|
||||
null, // "dstore_2",
|
||||
null, // "dstore_3",
|
||||
null, // "astore_0",
|
||||
null, // "astore_1",
|
||||
null, // "astore_2",
|
||||
null, // "astore_3",
|
||||
null, // "iastore",
|
||||
null, // "lastore",
|
||||
null, // "fastore",
|
||||
null, // "dastore",
|
||||
null, // "aastore",
|
||||
null, // "bastore",
|
||||
null, // "castore",
|
||||
null, // "sastore",
|
||||
null, // "pop",
|
||||
null, // "pop2",
|
||||
null, // "dup",
|
||||
null, // "dup_x1",
|
||||
null, // "dup_x2",
|
||||
null, // "dup2",
|
||||
null, // "dup2_x1",
|
||||
null, // "dup2_x2",
|
||||
null, // "swap",
|
||||
null, // "iadd",
|
||||
null, // "ladd",
|
||||
null, // "fadd",
|
||||
null, // "dadd",
|
||||
null, // "isub",
|
||||
null, // "lsub",
|
||||
null, // "fsub",
|
||||
null, // "dsub",
|
||||
null, // "imul",
|
||||
null, // "lmul",
|
||||
null, // "fmul",
|
||||
null, // "dmul",
|
||||
null, // "idiv",
|
||||
null, // "ldiv",
|
||||
null, // "fdiv",
|
||||
null, // "ddiv",
|
||||
null, // "irem",
|
||||
null, // "lrem",
|
||||
null, // "frem",
|
||||
null, // "drem",
|
||||
null, // "ineg",
|
||||
null, // "lneg",
|
||||
null, // "fneg",
|
||||
null, // "dneg",
|
||||
null, // "ishl",
|
||||
null, // "lshl",
|
||||
null, // "ishr",
|
||||
null, // "lshr",
|
||||
null, // "iushr",
|
||||
null, // "lushr",
|
||||
null, // "iand",
|
||||
null, // "land",
|
||||
null, // "ior",
|
||||
null, // "lor",
|
||||
null, // "ixor",
|
||||
null, // "lxor",
|
||||
IINC.class, // "iinc",
|
||||
null, // "i2l",
|
||||
null, // "i2f",
|
||||
null, // "i2d",
|
||||
null, // "l2i",
|
||||
null, // "l2f",
|
||||
null, // "l2d",
|
||||
null, // "f2i",
|
||||
null, // "f2l",
|
||||
null, // "f2d",
|
||||
null, // "d2i",
|
||||
null, // "d2l",
|
||||
null, // "d2f",
|
||||
null, // "i2b",
|
||||
null, // "i2c",
|
||||
null, // "i2s",
|
||||
null, // "lcmp",
|
||||
null, // "fcmpl",
|
||||
null, // "fcmpg",
|
||||
null, // "dcmpl",
|
||||
null, // "dcmpg",
|
||||
null, // "ifeq",
|
||||
null, // "ifne",
|
||||
null, // "iflt",
|
||||
null, // "ifge",
|
||||
null, // "ifgt",
|
||||
null, // "ifle",
|
||||
null, // "if_icmpeq",
|
||||
null, // "if_icmpne",
|
||||
null, // "if_icmplt",
|
||||
null, // "if_icmpge",
|
||||
null, // "if_icmpgt",
|
||||
null, // "if_icmple",
|
||||
null, // "if_acmpeq",
|
||||
null, // "if_acmpne",
|
||||
GOTO.class, // "goto",
|
||||
JSR.class, // "jsr",
|
||||
RET.class, // "ret",
|
||||
TABLESWITCH.class, // "tableswitch",
|
||||
LOOKUPSWITCH.class, // "lookupswitch",
|
||||
null, // "ireturn",
|
||||
null, // "lreturn",
|
||||
null, // "freturn",
|
||||
null, // "dreturn",
|
||||
null, // "areturn",
|
||||
null, // "return",
|
||||
GETSTATIC.class, // "getstatic",
|
||||
PUTSTATIC.class, // "putstatic",
|
||||
GETFIELD.class, // "getfield",
|
||||
PUTFIELD.class, // "putfield",
|
||||
INVOKEVIRTUAL.class, // "invokevirtual",
|
||||
INVOKESPECIAL.class, // "invokespecial",
|
||||
INVOKESTATIC.class, // "invokestatic",
|
||||
INVOKEINTERFACE.class, // "invokeinterface",
|
||||
INVOKEDYNAMIC.class, // "xxxunusedxxx" Java 6 and before, "invokedynamic" Java 7 and later
|
||||
NEW.class, // "new",
|
||||
NEWARRAY.class, // "newarray",
|
||||
ANEWARRAY.class, // "anewarray",
|
||||
null, // "arraylength",
|
||||
null, // "athrow",
|
||||
CHECKCAST.class, // "checkcast",
|
||||
INSTANCEOF.class, // "instanceof",
|
||||
null, // "monitorenter",
|
||||
null, // "monitorexit",
|
||||
null, // "wide",
|
||||
MULTIANEWARRAY.class, // "multianewarray",
|
||||
null, // "ifnull",
|
||||
null, // "ifnonnull",
|
||||
GOTO_W.class, // "goto_w",
|
||||
JSR_W.class // "jsr_w"
|
||||
};
|
||||
}
|
||||
@@ -1,27 +1,9 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.code;
|
||||
|
||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ExceptionHandler {
|
||||
|
||||
public int from = 0;
|
||||
public int to = 0;
|
||||
public int handler = 0;
|
||||
@@ -30,32 +12,12 @@ public class ExceptionHandler {
|
||||
public int to_instr = 0;
|
||||
public int handler_instr = 0;
|
||||
|
||||
public int class_index = 0;
|
||||
public String exceptionClass = null;
|
||||
|
||||
public ExceptionHandler() {
|
||||
}
|
||||
|
||||
public ExceptionHandler(int from_raw, int to_raw, int handler_raw, String exceptionClass) {
|
||||
this.from = from_raw;
|
||||
this.to = to_raw;
|
||||
this.handler = handler_raw;
|
||||
this.exceptionClass = exceptionClass;
|
||||
}
|
||||
|
||||
public void writeToStream(DataOutputStream out) throws IOException {
|
||||
out.writeShort(from);
|
||||
out.writeShort(to);
|
||||
out.writeShort(handler);
|
||||
out.writeShort(class_index);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
String new_line_separator = DecompilerContext.getNewLineSeparator();
|
||||
|
||||
return "from: " + from + " to: " + to + " handler: " + handler + new_line_separator +
|
||||
"from_instr: " + from_instr + " to_instr: " + to_instr + " handler_instr: " + handler_instr + new_line_separator +
|
||||
"exceptionClass: " + exceptionClass + new_line_separator;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,11 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.code;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.interpreter.Util;
|
||||
import org.jetbrains.java.decompiler.struct.StructContext;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ExceptionTable {
|
||||
public static final ExceptionTable EMPTY = new ExceptionTable(null) {
|
||||
@Override
|
||||
public List<ExceptionHandler> getHandlers() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
};
|
||||
public static final ExceptionTable EMPTY = new ExceptionTable(Collections.emptyList());
|
||||
|
||||
private final List<ExceptionHandler> handlers;
|
||||
|
||||
@@ -35,27 +13,7 @@ public class ExceptionTable {
|
||||
this.handlers = handlers;
|
||||
}
|
||||
|
||||
|
||||
public ExceptionHandler getHandlerByClass(StructContext context, int line, String valclass, boolean withany) {
|
||||
|
||||
ExceptionHandler res = null; // no handler found
|
||||
|
||||
for (ExceptionHandler handler : handlers) {
|
||||
if (handler.from <= line && handler.to > line) {
|
||||
String name = handler.exceptionClass;
|
||||
|
||||
if ((withany && name == null) || // any -> finally or synchronized handler
|
||||
(name != null && Util.instanceOf(context, valclass, name))) {
|
||||
res = handler;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public List<ExceptionHandler> getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.code;
|
||||
|
||||
import org.jetbrains.java.decompiler.util.VBStyleCollection;
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
* opc_ifeq, opc_ifne, opc_iflt, opc_ifge, opc_ifgt, opc_ifle, opc_if_icmpeq, opc_if_icmpne, opc_if_icmplt,
|
||||
* opc_if_icmpge, opc_if_icmpgt, opc_if_icmple, opc_if_acmpeq, opc_if_acmpne, opc_ifnull, opc_ifnonnull
|
||||
*/
|
||||
|
||||
public class IfInstruction extends JumpInstruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opcode);
|
||||
out.writeShort(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
@@ -1,126 +1,87 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.code;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.java.decompiler.util.TextUtil;
|
||||
|
||||
public class Instruction implements CodeConstants {
|
||||
|
||||
// *****************************************************************************
|
||||
// public fields
|
||||
// *****************************************************************************
|
||||
|
||||
public int opcode;
|
||||
|
||||
public int group = CodeConstants.GROUP_GENERAL;
|
||||
|
||||
public boolean wide = false;
|
||||
|
||||
public int bytecode_version = BYTECODE_JAVA_LE_4;
|
||||
|
||||
// *****************************************************************************
|
||||
// private fields
|
||||
// *****************************************************************************
|
||||
|
||||
private int[] operands = null;
|
||||
|
||||
// *****************************************************************************
|
||||
// public methods
|
||||
// *****************************************************************************
|
||||
|
||||
public Instruction() {
|
||||
public static Instruction create(int opcode, boolean wide, int group, int bytecodeVersion, int[] operands) {
|
||||
if (opcode >= opc_ifeq && opcode <= opc_if_acmpne ||
|
||||
opcode == opc_ifnull || opcode == opc_ifnonnull ||
|
||||
opcode == opc_jsr || opcode == opc_jsr_w ||
|
||||
opcode == opc_goto || opcode == opc_goto_w) {
|
||||
return new JumpInstruction(opcode, group, wide, bytecodeVersion, operands);
|
||||
}
|
||||
else if (opcode == opc_tableswitch || opcode == opc_lookupswitch) {
|
||||
return new SwitchInstruction(opcode, group, wide, bytecodeVersion, operands);
|
||||
}
|
||||
else {
|
||||
return new Instruction(opcode, group, wide, bytecodeVersion, operands);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 1;
|
||||
public static boolean equals(Instruction i1, Instruction i2) {
|
||||
return i1 != null && i2 != null &&
|
||||
(i1 == i2 ||
|
||||
i1.opcode == i2.opcode &&
|
||||
i1.wide == i2.wide &&
|
||||
i1.operandsCount() == i2.operandsCount());
|
||||
}
|
||||
|
||||
public final int opcode;
|
||||
public final int group;
|
||||
public final boolean wide;
|
||||
public final int bytecodeVersion;
|
||||
|
||||
protected final int[] operands;
|
||||
|
||||
public Instruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) {
|
||||
this.opcode = opcode;
|
||||
this.group = group;
|
||||
this.wide = wide;
|
||||
this.bytecodeVersion = bytecodeVersion;
|
||||
this.operands = operands;
|
||||
}
|
||||
|
||||
public void initInstruction(InstructionSequence seq) { }
|
||||
|
||||
public int operandsCount() {
|
||||
return (operands == null) ? 0 : operands.length;
|
||||
return operands == null ? 0 : operands.length;
|
||||
}
|
||||
|
||||
public int getOperand(int index) {
|
||||
public int operand(int index) {
|
||||
return operands[index];
|
||||
}
|
||||
|
||||
public Instruction clone() {
|
||||
return ConstantsUtil.getInstructionInstance(opcode, wide, group, bytecode_version, operands == null ? null : operands.clone());
|
||||
public boolean canFallThrough() {
|
||||
return opcode != opc_goto && opcode != opc_goto_w && opcode != opc_ret &&
|
||||
!(opcode >= opc_ireturn && opcode <= opc_return) &&
|
||||
opcode != opc_athrow &&
|
||||
opcode != opc_jsr && opcode != opc_tableswitch && opcode != opc_lookupswitch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String res = wide ? "@wide " : "";
|
||||
res += "@" + ConstantsUtil.getName(opcode);
|
||||
StringBuilder res = new StringBuilder();
|
||||
if (wide) res.append("@wide ");
|
||||
res.append("@").append(TextUtil.getInstructionName(opcode));
|
||||
|
||||
int len = operandsCount();
|
||||
for (int i = 0; i < len; i++) {
|
||||
int op = operands[i];
|
||||
if (op < 0) {
|
||||
res += " -" + Integer.toHexString(-op);
|
||||
res.append(" -").append(Integer.toHexString(-op));
|
||||
}
|
||||
else {
|
||||
res += " " + Integer.toHexString(op);
|
||||
res.append(" ").append(Integer.toHexString(op));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
public boolean canFallthrough() {
|
||||
return opcode != opc_goto && opcode != opc_goto_w && opcode != opc_ret &&
|
||||
!(opcode >= opc_ireturn && opcode <= opc_return) && opcode != opc_athrow
|
||||
&& opcode != opc_jsr && opcode != opc_tableswitch && opcode != opc_lookupswitch;
|
||||
@Override
|
||||
@SuppressWarnings("MethodDoesntCallSuperMethod")
|
||||
public Instruction clone() {
|
||||
return create(opcode, wide, group, bytecodeVersion, operands == null ? null : operands.clone());
|
||||
}
|
||||
|
||||
public boolean equalsInstruction(Instruction instr) {
|
||||
if (opcode != instr.opcode || wide != instr.wide
|
||||
|| operandsCount() != instr.operandsCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (operands != null) {
|
||||
for (int i = 0; i < operands.length; i++) {
|
||||
if (operands[i] != instr.getOperand(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// should be overwritten by subclasses
|
||||
public void initInstruction(InstructionSequence seq) {
|
||||
}
|
||||
|
||||
// should be overwritten by subclasses
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opcode);
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// getter and setter methods
|
||||
// *****************************************************************************
|
||||
|
||||
public int[] getOperands() {
|
||||
return operands;
|
||||
}
|
||||
|
||||
public void setOperands(int[] operands) {
|
||||
this.operands = operands;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,10 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.code;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.interpreter.Util;
|
||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||
import org.jetbrains.java.decompiler.struct.StructContext;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
import org.jetbrains.java.decompiler.util.TextUtil;
|
||||
import org.jetbrains.java.decompiler.util.VBStyleCollection;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public abstract class InstructionSequence {
|
||||
|
||||
// *****************************************************************************
|
||||
@@ -41,7 +18,7 @@ public abstract class InstructionSequence {
|
||||
protected ExceptionTable exceptionTable = ExceptionTable.EMPTY;
|
||||
|
||||
protected InstructionSequence() {
|
||||
this(new VBStyleCollection<Instruction, Integer>());
|
||||
this(new VBStyleCollection<>());
|
||||
}
|
||||
|
||||
protected InstructionSequence(VBStyleCollection<Instruction, Integer> collinstr) {
|
||||
@@ -53,6 +30,7 @@ public abstract class InstructionSequence {
|
||||
// *****************************************************************************
|
||||
|
||||
// to nbe overwritten
|
||||
@Override
|
||||
public InstructionSequence clone() {
|
||||
return null;
|
||||
}
|
||||
@@ -87,10 +65,6 @@ public abstract class InstructionSequence {
|
||||
}
|
||||
}
|
||||
|
||||
public Instruction getCurrentInstr() {
|
||||
return collinstr.get(pointer);
|
||||
}
|
||||
|
||||
public Instruction getInstr(int index) {
|
||||
return collinstr.get(index);
|
||||
}
|
||||
@@ -99,16 +73,12 @@ public abstract class InstructionSequence {
|
||||
return collinstr.getLast();
|
||||
}
|
||||
|
||||
public int getCurrentOffset() {
|
||||
return collinstr.getKey(pointer).intValue();
|
||||
}
|
||||
|
||||
public int getOffset(int index) {
|
||||
return collinstr.getKey(index).intValue();
|
||||
public int getOffset(int index) {
|
||||
return collinstr.getKey(index);
|
||||
}
|
||||
|
||||
public int getPointerByAbsOffset(int offset) {
|
||||
Integer absoffset = new Integer(offset);
|
||||
Integer absoffset = offset;
|
||||
if (collinstr.containsKey(absoffset)) {
|
||||
return collinstr.getIndexByKey(absoffset);
|
||||
}
|
||||
@@ -118,7 +88,7 @@ public int getOffset(int index) {
|
||||
}
|
||||
|
||||
public int getPointerByRelOffset(int offset) {
|
||||
Integer absoffset = new Integer(collinstr.getKey(pointer).intValue() + offset);
|
||||
Integer absoffset = collinstr.getKey(pointer) + offset;
|
||||
if (collinstr.containsKey(absoffset)) {
|
||||
return collinstr.getIndexByKey(absoffset);
|
||||
}
|
||||
@@ -127,13 +97,6 @@ public int getOffset(int index) {
|
||||
}
|
||||
}
|
||||
|
||||
public void setPointerByAbsOffset(int offset) {
|
||||
Integer absoffset = new Integer(collinstr.getKey(pointer).intValue() + offset);
|
||||
if (collinstr.containsKey(absoffset)) {
|
||||
pointer = collinstr.getIndexByKey(absoffset);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return collinstr.size();
|
||||
}
|
||||
@@ -157,7 +120,7 @@ public int getOffset(int index) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < collinstr.size(); i++) {
|
||||
buf.append(InterpreterUtil.getIndentString(indent));
|
||||
buf.append(TextUtil.getIndentString(indent));
|
||||
buf.append(collinstr.getKey(i).intValue());
|
||||
buf.append(": ");
|
||||
buf.append(collinstr.get(i).toString());
|
||||
@@ -167,58 +130,6 @@ public int getOffset(int index) {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public void writeCodeToStream(DataOutputStream out) throws IOException {
|
||||
|
||||
for (int i = 0; i < collinstr.size(); i++) {
|
||||
collinstr.get(i).writeToStream(out, collinstr.getKey(i).intValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void writeExceptionsToStream(DataOutputStream out) throws IOException {
|
||||
|
||||
List<ExceptionHandler> handlers = exceptionTable.getHandlers();
|
||||
|
||||
out.writeShort(handlers.size());
|
||||
for (int i = 0; i < handlers.size(); i++) {
|
||||
handlers.get(i).writeToStream(out);
|
||||
}
|
||||
}
|
||||
|
||||
public void sortHandlers(final StructContext context) {
|
||||
|
||||
Collections.sort(exceptionTable.getHandlers(), new Comparator<ExceptionHandler>() {
|
||||
|
||||
public int compare(ExceptionHandler handler0, ExceptionHandler handler1) {
|
||||
|
||||
if (handler0.to == handler1.to) {
|
||||
if (handler0.exceptionClass == null) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
if (handler1.exceptionClass == null) {
|
||||
return -1;
|
||||
}
|
||||
else if (handler0.exceptionClass.equals(handler1.exceptionClass)) {
|
||||
return (handler0.from > handler1.from) ? -1 : 1; // invalid code
|
||||
}
|
||||
else {
|
||||
if (Util.instanceOf(context, handler0.exceptionClass, handler1.exceptionClass)) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return (handler0.to > handler1.to) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// *****************************************************************************
|
||||
// getter and setter methods
|
||||
// *****************************************************************************
|
||||
@@ -234,8 +145,4 @@ public int getOffset(int index) {
|
||||
public ExceptionTable getExceptionTable() {
|
||||
return exceptionTable;
|
||||
}
|
||||
|
||||
public void setExceptionTable(ExceptionTable exceptionTable) {
|
||||
this.exceptionTable = exceptionTable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,22 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.code;
|
||||
|
||||
/*
|
||||
* opc_ifeq, opc_ifne, opc_iflt, opc_ifge, opc_ifgt, opc_ifle, opc_if_icmpeq, opc_if_icmpne, opc_if_icmplt,
|
||||
* opc_if_icmpge, opc_if_icmpgt, opc_if_icmple, opc_if_acmpeq, opc_if_acmpne, opc_ifnull, opc_ifnonnull
|
||||
* opc_goto, opc_jsr, opc_goto_w, opc_jsr_w
|
||||
*/
|
||||
|
||||
|
||||
public class JumpInstruction extends Instruction {
|
||||
|
||||
public int destination;
|
||||
|
||||
public JumpInstruction() {
|
||||
public JumpInstruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) {
|
||||
super(opcode, group, wide, bytecodeVersion, operands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initInstruction(InstructionSequence seq) {
|
||||
destination = seq.getPointerByRelOffset(this.getOperand(0));
|
||||
destination = seq.getPointerByRelOffset(this.operand(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JumpInstruction clone() {
|
||||
JumpInstruction newinstr = (JumpInstruction)super.clone();
|
||||
|
||||
newinstr.destination = destination;
|
||||
return newinstr;
|
||||
JumpInstruction copy = (JumpInstruction)super.clone();
|
||||
copy.destination = destination;
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.code;
|
||||
|
||||
import org.jetbrains.java.decompiler.util.VBStyleCollection;
|
||||
@@ -26,14 +12,11 @@ public class SimpleInstructionSequence extends InstructionSequence {
|
||||
super(collinstr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleInstructionSequence clone() {
|
||||
SimpleInstructionSequence newseq = new SimpleInstructionSequence(collinstr.clone());
|
||||
newseq.setPointer(this.getPointer());
|
||||
|
||||
return newseq;
|
||||
}
|
||||
|
||||
public void removeInstruction(int index) {
|
||||
collinstr.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +1,61 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.code;
|
||||
|
||||
/*
|
||||
* opc_tableswitch, lookupswitch
|
||||
*/
|
||||
|
||||
public class SwitchInstruction extends Instruction {
|
||||
|
||||
private int[] destinations;
|
||||
|
||||
private int[] values;
|
||||
private int defaultDestination;
|
||||
|
||||
private int defaultdest;
|
||||
|
||||
public SwitchInstruction() {
|
||||
public SwitchInstruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) {
|
||||
super(opcode, group, wide, bytecodeVersion, operands);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void initInstruction(InstructionSequence seq) {
|
||||
defaultDestination = seq.getPointerByRelOffset(operands[0]);
|
||||
|
||||
int pref = (opcode == CodeConstants.opc_tableswitch ? 3 : 2);
|
||||
int len = this.getOperands().length - pref;
|
||||
defaultdest = seq.getPointerByRelOffset(this.getOperand(0));
|
||||
|
||||
int prefix = opcode == CodeConstants.opc_tableswitch ? 3 : 2;
|
||||
int len = operands.length - prefix;
|
||||
int low = 0;
|
||||
|
||||
if (opcode == CodeConstants.opc_lookupswitch) {
|
||||
len /= 2;
|
||||
}
|
||||
else {
|
||||
low = this.getOperand(1);
|
||||
low = operands[1];
|
||||
}
|
||||
|
||||
destinations = new int[len];
|
||||
values = new int[len];
|
||||
|
||||
for (int i = 0, k = 0; i < len; i++, k++) {
|
||||
if (opcode == CodeConstants.opc_lookupswitch) {
|
||||
values[i] = this.getOperand(pref + k);
|
||||
values[i] = operands[prefix + k];
|
||||
k++;
|
||||
}
|
||||
else {
|
||||
values[i] = low + k;
|
||||
}
|
||||
destinations[i] = seq.getPointerByRelOffset(this.getOperand(pref + k));
|
||||
destinations[i] = seq.getPointerByRelOffset(operands[prefix + k]);
|
||||
}
|
||||
}
|
||||
|
||||
public SwitchInstruction clone() {
|
||||
SwitchInstruction newinstr = (SwitchInstruction)super.clone();
|
||||
|
||||
newinstr.defaultdest = defaultdest;
|
||||
newinstr.destinations = destinations.clone();
|
||||
newinstr.values = values.clone();
|
||||
|
||||
return newinstr;
|
||||
}
|
||||
|
||||
public int[] getDestinations() {
|
||||
return destinations;
|
||||
}
|
||||
|
||||
public void setDestinations(int[] destinations) {
|
||||
this.destinations = destinations;
|
||||
}
|
||||
|
||||
public int getDefaultdest() {
|
||||
return defaultdest;
|
||||
}
|
||||
|
||||
public void setDefaultdest(int defaultdest) {
|
||||
this.defaultdest = defaultdest;
|
||||
}
|
||||
|
||||
public int[] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public void setValues(int[] values) {
|
||||
this.values = values;
|
||||
public int getDefaultDestination() {
|
||||
return defaultDestination;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SwitchInstruction clone() {
|
||||
SwitchInstruction copy = (SwitchInstruction)super.clone();
|
||||
copy.defaultDestination = defaultDestination;
|
||||
copy.destinations = destinations.clone();
|
||||
copy.values = values.clone();
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.code.cfg;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
@@ -30,8 +16,7 @@ public class BasicBlock implements IGraphNode {
|
||||
// public fields
|
||||
// *****************************************************************************
|
||||
|
||||
public int id = 0;
|
||||
|
||||
public int id;
|
||||
public int mark = 0;
|
||||
|
||||
// *****************************************************************************
|
||||
@@ -40,15 +25,11 @@ public class BasicBlock implements IGraphNode {
|
||||
|
||||
private InstructionSequence seq = new SimpleInstructionSequence();
|
||||
|
||||
private List<BasicBlock> preds = new ArrayList<BasicBlock>();
|
||||
|
||||
private List<BasicBlock> succs = new ArrayList<BasicBlock>();
|
||||
|
||||
private final List<Integer> instrOldOffsets = new ArrayList<Integer>();
|
||||
|
||||
private List<BasicBlock> predExceptions = new ArrayList<BasicBlock>();
|
||||
|
||||
private List<BasicBlock> succExceptions = new ArrayList<BasicBlock>();
|
||||
private final List<BasicBlock> preds = new ArrayList<>();
|
||||
private final List<BasicBlock> succs = new ArrayList<>();
|
||||
private final List<Integer> instrOldOffsets = new ArrayList<>();
|
||||
private final List<BasicBlock> predExceptions = new ArrayList<>();
|
||||
private final List<BasicBlock> succExceptions = new ArrayList<>();
|
||||
|
||||
public BasicBlock(int id) {
|
||||
this.id = id;
|
||||
@@ -58,7 +39,9 @@ public class BasicBlock implements IGraphNode {
|
||||
// public methods
|
||||
// *****************************************************************************
|
||||
|
||||
public Object clone() {
|
||||
@Override
|
||||
@SuppressWarnings("MethodDoesntCallSuperMethod")
|
||||
public BasicBlock clone() {
|
||||
BasicBlock block = new BasicBlock(id);
|
||||
|
||||
block.setSeq(seq.clone());
|
||||
@@ -67,14 +50,6 @@ public class BasicBlock implements IGraphNode {
|
||||
return block;
|
||||
}
|
||||
|
||||
public void free() {
|
||||
preds.clear();
|
||||
succs.clear();
|
||||
instrOldOffsets.clear();
|
||||
succExceptions.clear();
|
||||
seq = new SimpleInstructionSequence();
|
||||
}
|
||||
|
||||
public Instruction getInstruction(int index) {
|
||||
return seq.getInstr(index);
|
||||
}
|
||||
@@ -105,7 +80,7 @@ public class BasicBlock implements IGraphNode {
|
||||
}
|
||||
|
||||
public void removePredecessor(BasicBlock block) {
|
||||
while (preds.remove(block)) ;
|
||||
while (preds.remove(block)) /**/;
|
||||
}
|
||||
|
||||
public void addSuccessor(BasicBlock block) {
|
||||
@@ -114,11 +89,11 @@ public class BasicBlock implements IGraphNode {
|
||||
}
|
||||
|
||||
public void removeSuccessor(BasicBlock block) {
|
||||
while (succs.remove(block)) ;
|
||||
while (succs.remove(block)) /**/;
|
||||
block.removePredecessor(this);
|
||||
}
|
||||
|
||||
// FIXME: unify block comparisons: id or direkt equality
|
||||
// FIXME: unify block comparisons: id or direct equality
|
||||
public void replaceSuccessor(BasicBlock oldBlock, BasicBlock newBlock) {
|
||||
for (int i = 0; i < succs.size(); i++) {
|
||||
if (succs.get(i).id == oldBlock.id) {
|
||||
@@ -142,7 +117,7 @@ public class BasicBlock implements IGraphNode {
|
||||
}
|
||||
|
||||
public void removePredecessorException(BasicBlock block) {
|
||||
while (predExceptions.remove(block)) ;
|
||||
while (predExceptions.remove(block)) /**/;
|
||||
}
|
||||
|
||||
public void addSuccessorException(BasicBlock block) {
|
||||
@@ -153,7 +128,7 @@ public class BasicBlock implements IGraphNode {
|
||||
}
|
||||
|
||||
public void removeSuccessorException(BasicBlock block) {
|
||||
while (succExceptions.remove(block)) ;
|
||||
while (succExceptions.remove(block)) /**/;
|
||||
block.removePredecessorException(this);
|
||||
}
|
||||
|
||||
@@ -168,27 +143,6 @@ public class BasicBlock implements IGraphNode {
|
||||
return id + ":" + new_line_separator + seq.toString(indent);
|
||||
}
|
||||
|
||||
public String toStringOldIndices() {
|
||||
|
||||
String new_line_separator = DecompilerContext.getNewLineSeparator();
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < seq.length(); i++) {
|
||||
if (i < instrOldOffsets.size()) {
|
||||
buf.append(instrOldOffsets.get(i));
|
||||
}
|
||||
else {
|
||||
buf.append("-1");
|
||||
}
|
||||
buf.append(": ");
|
||||
buf.append(seq.getInstr(i).toString());
|
||||
buf.append(new_line_separator);
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public boolean isSuccessor(BasicBlock block) {
|
||||
for (BasicBlock succ : succs) {
|
||||
if (succ.id == block.id) {
|
||||
@@ -198,15 +152,6 @@ public class BasicBlock implements IGraphNode {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isPredecessor(BasicBlock block) {
|
||||
for (int i = 0; i < preds.size(); i++) {
|
||||
if (preds.get(i).id == block.id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// getter and setter methods
|
||||
// *****************************************************************************
|
||||
@@ -215,8 +160,9 @@ public class BasicBlock implements IGraphNode {
|
||||
return instrOldOffsets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends IGraphNode> getPredecessors() {
|
||||
List<BasicBlock> lst = new ArrayList<BasicBlock>(preds);
|
||||
List<BasicBlock> lst = new ArrayList<>(preds);
|
||||
lst.addAll(predExceptions);
|
||||
return lst;
|
||||
}
|
||||
@@ -225,10 +171,6 @@ public class BasicBlock implements IGraphNode {
|
||||
return preds;
|
||||
}
|
||||
|
||||
public void setPreds(List<BasicBlock> preds) {
|
||||
this.preds = preds;
|
||||
}
|
||||
|
||||
public InstructionSequence getSeq() {
|
||||
return seq;
|
||||
}
|
||||
@@ -241,25 +183,11 @@ public class BasicBlock implements IGraphNode {
|
||||
return succs;
|
||||
}
|
||||
|
||||
public void setSuccs(List<BasicBlock> succs) {
|
||||
this.succs = succs;
|
||||
}
|
||||
|
||||
|
||||
public List<BasicBlock> getSuccExceptions() {
|
||||
return succExceptions;
|
||||
}
|
||||
|
||||
|
||||
public void setSuccExceptions(List<BasicBlock> succExceptions) {
|
||||
this.succExceptions = succExceptions;
|
||||
}
|
||||
|
||||
public List<BasicBlock> getPredExceptions() {
|
||||
return predExceptions;
|
||||
}
|
||||
|
||||
public void setPredExceptions(List<BasicBlock> predExceptions) {
|
||||
this.predExceptions = predExceptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.code.cfg;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.*;
|
||||
@@ -47,7 +33,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
|
||||
private Map<BasicBlock, BasicBlock> subroutines;
|
||||
|
||||
private Set<BasicBlock> finallyExits = new HashSet<BasicBlock>();
|
||||
private final Set<BasicBlock> finallyExits = new HashSet<>();
|
||||
|
||||
// *****************************************************************************
|
||||
// constructors
|
||||
@@ -62,19 +48,6 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
// public methods
|
||||
// *****************************************************************************
|
||||
|
||||
public void free() {
|
||||
|
||||
for (BasicBlock block : blocks) {
|
||||
block.free();
|
||||
}
|
||||
|
||||
blocks.clear();
|
||||
first = null;
|
||||
last = null;
|
||||
exceptions.clear();
|
||||
finallyExits.clear();
|
||||
}
|
||||
|
||||
public void removeMarkers() {
|
||||
for (BasicBlock block : blocks) {
|
||||
block.mark = 0;
|
||||
@@ -94,12 +67,11 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
buf.append("----- Edges -----").append(new_line_separator);
|
||||
|
||||
List<BasicBlock> suc = block.getSuccs();
|
||||
for (int j = 0; j < suc.size(); j++) {
|
||||
buf.append(">>>>>>>>(regular) Block ").append(suc.get(j).id).append(new_line_separator);
|
||||
for (BasicBlock aSuc : suc) {
|
||||
buf.append(">>>>>>>>(regular) Block ").append(aSuc.id).append(new_line_separator);
|
||||
}
|
||||
suc = block.getSuccExceptions();
|
||||
for (int j = 0; j < suc.size(); j++) {
|
||||
BasicBlock handler = suc.get(j);
|
||||
for (BasicBlock handler : suc) {
|
||||
ExceptionRangeCFG range = getExceptionRange(handler, block);
|
||||
|
||||
if (range == null) {
|
||||
@@ -170,13 +142,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
}
|
||||
}
|
||||
|
||||
Iterator<Entry<BasicBlock, BasicBlock>> it = subroutines.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry<BasicBlock, BasicBlock> ent = it.next();
|
||||
if (ent.getKey() == block || ent.getValue() == block) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
subroutines.entrySet().removeIf(ent -> ent.getKey() == block || ent.getValue() == block);
|
||||
}
|
||||
|
||||
public ExceptionRangeCFG getExceptionRange(BasicBlock handler, BasicBlock block) {
|
||||
@@ -225,7 +191,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
|
||||
short[] states = findStartInstructions(instrseq);
|
||||
|
||||
Map<Integer, BasicBlock> mapInstrBlocks = new HashMap<Integer, BasicBlock>();
|
||||
Map<Integer, BasicBlock> mapInstrBlocks = new HashMap<>();
|
||||
VBStyleCollection<BasicBlock, Integer> colBlocks = createBasicBlocks(states, instrseq, mapInstrBlocks);
|
||||
|
||||
blocks = colBlocks;
|
||||
@@ -244,7 +210,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
int len = seq.length();
|
||||
short[] inststates = new short[len];
|
||||
|
||||
Set<Integer> excSet = new HashSet<Integer>();
|
||||
Set<Integer> excSet = new HashSet<>();
|
||||
|
||||
for (ExceptionHandler handler : seq.getExceptionTable().getHandlers()) {
|
||||
excSet.add(handler.from_instr);
|
||||
@@ -256,7 +222,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
for (int i = 0; i < len; i++) {
|
||||
|
||||
// exception blocks
|
||||
if (excSet.contains(new Integer(i))) {
|
||||
if (excSet.contains(i)) {
|
||||
inststates[i] = 1;
|
||||
}
|
||||
|
||||
@@ -275,7 +241,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
for (int j = dests.length - 1; j >= 0; j--) {
|
||||
inststates[dests[j]] = 1;
|
||||
}
|
||||
inststates[swinstr.getDefaultdest()] = 1;
|
||||
inststates[swinstr.getDefaultDestination()] = 1;
|
||||
if (i + 1 < len) {
|
||||
inststates[i + 1] = 1;
|
||||
}
|
||||
@@ -293,7 +259,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
InstructionSequence instrseq,
|
||||
Map<Integer, BasicBlock> mapInstrBlocks) {
|
||||
|
||||
VBStyleCollection<BasicBlock, Integer> col = new VBStyleCollection<BasicBlock, Integer>();
|
||||
VBStyleCollection<BasicBlock, Integer> col = new VBStyleCollection<>();
|
||||
|
||||
InstructionSequence currseq = null;
|
||||
List<Integer> lstOffs = null;
|
||||
@@ -336,7 +302,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
BasicBlock block = lstbb.get(i);
|
||||
Instruction instr = block.getLastInstruction();
|
||||
|
||||
boolean fallthrough = instr.canFallthrough();
|
||||
boolean fallthrough = instr.canFallThrough();
|
||||
BasicBlock bTemp;
|
||||
|
||||
switch (instr.group) {
|
||||
@@ -350,10 +316,10 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
SwitchInstruction sinstr = (SwitchInstruction)instr;
|
||||
int[] dests = sinstr.getDestinations();
|
||||
|
||||
bTemp = mapInstrBlocks.get(((SwitchInstruction)instr).getDefaultdest());
|
||||
bTemp = mapInstrBlocks.get(((SwitchInstruction)instr).getDefaultDestination());
|
||||
block.addSuccessor(bTemp);
|
||||
for (int j = 0; j < dests.length; j++) {
|
||||
bTemp = mapInstrBlocks.get(dests[j]);
|
||||
for (int dest1 : dests) {
|
||||
bTemp = mapInstrBlocks.get(dest1);
|
||||
block.addSuccessor(bTemp);
|
||||
}
|
||||
}
|
||||
@@ -367,9 +333,9 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
|
||||
private void setExceptionEdges(InstructionSequence instrseq, Map<Integer, BasicBlock> instrBlocks) {
|
||||
|
||||
exceptions = new ArrayList<ExceptionRangeCFG>();
|
||||
exceptions = new ArrayList<>();
|
||||
|
||||
Map<String, ExceptionRangeCFG> mapRanges = new HashMap<String, ExceptionRangeCFG>();
|
||||
Map<String, ExceptionRangeCFG> mapRanges = new HashMap<>();
|
||||
|
||||
for (ExceptionHandler handler : instrseq.getExceptionTable().getHandlers()) {
|
||||
|
||||
@@ -385,7 +351,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
}
|
||||
else {
|
||||
|
||||
List<BasicBlock> protectedRange = new ArrayList<BasicBlock>();
|
||||
List<BasicBlock> protectedRange = new ArrayList<>();
|
||||
for (int j = from.id; j < to.id; j++) {
|
||||
BasicBlock block = blocks.getWithKey(j);
|
||||
protectedRange.add(block);
|
||||
@@ -404,19 +370,19 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
|
||||
private void setSubroutineEdges() {
|
||||
|
||||
final Map<BasicBlock, BasicBlock> subroutines = new HashMap<BasicBlock, BasicBlock>();
|
||||
final Map<BasicBlock, BasicBlock> subroutines = new LinkedHashMap<>();
|
||||
|
||||
for (BasicBlock block : blocks) {
|
||||
|
||||
if (block.getSeq().getLastInstr().opcode == CodeConstants.opc_jsr) {
|
||||
|
||||
LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>();
|
||||
LinkedList<LinkedList<BasicBlock>> stackJsrStacks = new LinkedList<LinkedList<BasicBlock>>();
|
||||
LinkedList<BasicBlock> stack = new LinkedList<>();
|
||||
LinkedList<LinkedList<BasicBlock>> stackJsrStacks = new LinkedList<>();
|
||||
|
||||
Set<BasicBlock> setVisited = new HashSet<BasicBlock>();
|
||||
Set<BasicBlock> setVisited = new HashSet<>();
|
||||
|
||||
stack.add(block);
|
||||
stackJsrStacks.add(new LinkedList<BasicBlock>());
|
||||
stackJsrStacks.add(new LinkedList<>());
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
|
||||
@@ -449,7 +415,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
for (BasicBlock succ : node.getSuccs()) {
|
||||
if (!setVisited.contains(succ)) {
|
||||
stack.add(succ);
|
||||
stackJsrStacks.add(new LinkedList<BasicBlock>(jsrstack));
|
||||
stackJsrStacks.add(new LinkedList<>(jsrstack));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -480,7 +446,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
|
||||
private int processJsrRanges() {
|
||||
|
||||
List<JsrRecord> lstJsrAll = new ArrayList<JsrRecord>();
|
||||
List<JsrRecord> lstJsrAll = new ArrayList<>();
|
||||
|
||||
// get all jsr ranges
|
||||
for (Entry<BasicBlock, BasicBlock> ent : subroutines.entrySet()) {
|
||||
@@ -492,7 +458,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
|
||||
// sort ranges
|
||||
// FIXME: better sort order
|
||||
List<JsrRecord> lstJsr = new ArrayList<JsrRecord>();
|
||||
List<JsrRecord> lstJsr = new ArrayList<>();
|
||||
for (JsrRecord arr : lstJsrAll) {
|
||||
int i = 0;
|
||||
for (; i < lstJsr.size(); i++) {
|
||||
@@ -514,7 +480,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
Set<BasicBlock> set1 = arr1.range;
|
||||
|
||||
if (!set.contains(arr1.jsr) && !set1.contains(arr.jsr)) { // rang 0 doesn't contain entry 1 and vice versa
|
||||
Set<BasicBlock> setc = new HashSet<BasicBlock>(set);
|
||||
Set<BasicBlock> setc = new HashSet<>(set);
|
||||
setc.retainAll(set1);
|
||||
|
||||
if (!setc.isEmpty()) {
|
||||
@@ -530,9 +496,9 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
|
||||
private Set<BasicBlock> getJsrRange(BasicBlock jsr, BasicBlock ret) {
|
||||
|
||||
Set<BasicBlock> blocks = new HashSet<BasicBlock>();
|
||||
Set<BasicBlock> blocks = new HashSet<>();
|
||||
|
||||
List<BasicBlock> lstNodes = new LinkedList<BasicBlock>();
|
||||
List<BasicBlock> lstNodes = new LinkedList<>();
|
||||
lstNodes.add(jsr);
|
||||
|
||||
BasicBlock dom = jsr.getSuccs().get(0);
|
||||
@@ -594,8 +560,8 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
|
||||
private void splitJsrRange(BasicBlock jsr, BasicBlock ret, Set<BasicBlock> common_blocks) {
|
||||
|
||||
List<BasicBlock> lstNodes = new LinkedList<BasicBlock>();
|
||||
Map<Integer, BasicBlock> mapNewNodes = new HashMap<Integer, BasicBlock>();
|
||||
List<BasicBlock> lstNodes = new LinkedList<>();
|
||||
Map<Integer, BasicBlock> mapNewNodes = new HashMap<>();
|
||||
|
||||
lstNodes.add(jsr);
|
||||
mapNewNodes.put(jsr.id, jsr);
|
||||
@@ -631,9 +597,8 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
node.replaceSuccessor(child, mapNewNodes.get(childid));
|
||||
}
|
||||
else if (common_blocks.contains(child)) {
|
||||
|
||||
// make a copy of the current block
|
||||
BasicBlock copy = (BasicBlock)child.clone();
|
||||
BasicBlock copy = child.clone();
|
||||
copy.id = ++last_id;
|
||||
// copy all successors
|
||||
if (copy.getLastInstruction().opcode == CodeConstants.opc_ret &&
|
||||
@@ -680,14 +645,14 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
ExceptionRangeCFG range = exceptions.get(i);
|
||||
List<BasicBlock> lstRange = range.getProtectedRange();
|
||||
|
||||
HashSet<BasicBlock> setBoth = new HashSet<BasicBlock>(common_blocks);
|
||||
HashSet<BasicBlock> setBoth = new HashSet<>(common_blocks);
|
||||
setBoth.retainAll(lstRange);
|
||||
|
||||
if (setBoth.size() > 0) {
|
||||
List<BasicBlock> lstNewRange;
|
||||
|
||||
if (setBoth.size() == lstRange.size()) {
|
||||
lstNewRange = new ArrayList<BasicBlock>();
|
||||
lstNewRange = new ArrayList<>();
|
||||
ExceptionRangeCFG newRange = new ExceptionRangeCFG(lstNewRange,
|
||||
mapNewNodes.get(range.getHandler().id), range.getExceptionTypes());
|
||||
exceptions.add(newRange);
|
||||
@@ -751,7 +716,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
if (suc.mark != 1) {
|
||||
|
||||
DataPoint point = new DataPoint();
|
||||
point.setLocalVariables(new ArrayList<VarType>(data.getLocalVariables()));
|
||||
point.setLocalVariables(new ArrayList<>(data.getLocalVariables()));
|
||||
point.getStack().push(new VarType(CodeConstants.TYPE_OBJECT, 0, null));
|
||||
|
||||
removeJsrInstructions(pool, suc, point);
|
||||
@@ -774,18 +739,18 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
|
||||
public List<BasicBlock> getReversePostOrder() {
|
||||
|
||||
List<BasicBlock> res = new LinkedList<BasicBlock>();
|
||||
List<BasicBlock> res = new LinkedList<>();
|
||||
addToReversePostOrderListIterative(first, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void addToReversePostOrderListIterative(BasicBlock root, List<BasicBlock> lst) {
|
||||
private static void addToReversePostOrderListIterative(BasicBlock root, List<? super BasicBlock> lst) {
|
||||
|
||||
LinkedList<BasicBlock> stackNode = new LinkedList<BasicBlock>();
|
||||
LinkedList<Integer> stackIndex = new LinkedList<Integer>();
|
||||
LinkedList<BasicBlock> stackNode = new LinkedList<>();
|
||||
LinkedList<Integer> stackIndex = new LinkedList<>();
|
||||
|
||||
Set<BasicBlock> setVisited = new HashSet<BasicBlock>();
|
||||
Set<BasicBlock> setVisited = new HashSet<>();
|
||||
|
||||
stackNode.add(root);
|
||||
stackIndex.add(0);
|
||||
@@ -797,7 +762,7 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
|
||||
setVisited.add(node);
|
||||
|
||||
List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(node.getSuccs());
|
||||
List<BasicBlock> lstSuccs = new ArrayList<>(node.getSuccs());
|
||||
lstSuccs.addAll(node.getSuccExceptions());
|
||||
|
||||
for (; index < lstSuccs.size(); index++) {
|
||||
@@ -830,10 +795,6 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
return blocks;
|
||||
}
|
||||
|
||||
public void setBlocks(VBStyleCollection<BasicBlock, Integer> blocks) {
|
||||
this.blocks = blocks;
|
||||
}
|
||||
|
||||
public BasicBlock getFirst() {
|
||||
return first;
|
||||
}
|
||||
@@ -842,39 +803,15 @@ public class ControlFlowGraph implements CodeConstants {
|
||||
this.first = first;
|
||||
}
|
||||
|
||||
public List<BasicBlock> getEndBlocks() {
|
||||
return last.getPreds();
|
||||
}
|
||||
|
||||
public List<ExceptionRangeCFG> getExceptions() {
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
public void setExceptions(List<ExceptionRangeCFG> exceptions) {
|
||||
this.exceptions = exceptions;
|
||||
}
|
||||
|
||||
public BasicBlock getLast() {
|
||||
return last;
|
||||
}
|
||||
|
||||
public void setLast(BasicBlock last) {
|
||||
this.last = last;
|
||||
}
|
||||
|
||||
public Map<BasicBlock, BasicBlock> getSubroutines() {
|
||||
return subroutines;
|
||||
}
|
||||
|
||||
public void setSubroutines(Map<BasicBlock, BasicBlock> subroutines) {
|
||||
this.subroutines = subroutines;
|
||||
}
|
||||
|
||||
public Set<BasicBlock> getFinallyExits() {
|
||||
return finallyExits;
|
||||
}
|
||||
|
||||
public void setFinallyExits(HashSet<BasicBlock> finallyExits) {
|
||||
this.finallyExits = finallyExits;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,15 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.code.cfg;
|
||||
|
||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ExceptionRangeCFG {
|
||||
|
||||
private List<BasicBlock> protectedRange = new ArrayList<BasicBlock>(); // FIXME: replace with set
|
||||
|
||||
private final List<BasicBlock> protectedRange; // FIXME: replace with set
|
||||
private BasicBlock handler;
|
||||
|
||||
private List<String> exceptionTypes;
|
||||
|
||||
public ExceptionRangeCFG(List<BasicBlock> protectedRange, BasicBlock handler, List<String> exceptionType) {
|
||||
@@ -35,7 +17,7 @@ public class ExceptionRangeCFG {
|
||||
this.handler = handler;
|
||||
|
||||
if (exceptionType != null) {
|
||||
this.exceptionTypes = new ArrayList<String>(exceptionType);
|
||||
this.exceptionTypes = new ArrayList<>(exceptionType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,10 +25,9 @@ public class ExceptionRangeCFG {
|
||||
return protectedRange.contains(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String new_line_separator = DecompilerContext.getNewLineSeparator();
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
buf.append("exceptionType:");
|
||||
@@ -57,8 +38,8 @@ public class ExceptionRangeCFG {
|
||||
|
||||
buf.append("handler: ").append(handler.id).append(new_line_separator);
|
||||
buf.append("range: ");
|
||||
for (int i = 0; i < protectedRange.size(); i++) {
|
||||
buf.append(protectedRange.get(i).id).append(" ");
|
||||
for (BasicBlock block : protectedRange) {
|
||||
buf.append(block.id).append(" ");
|
||||
}
|
||||
buf.append(new_line_separator);
|
||||
|
||||
@@ -77,16 +58,11 @@ public class ExceptionRangeCFG {
|
||||
return protectedRange;
|
||||
}
|
||||
|
||||
public void setProtectedRange(List<BasicBlock> protectedRange) {
|
||||
this.protectedRange = protectedRange;
|
||||
}
|
||||
|
||||
public List<String> getExceptionTypes() {
|
||||
return this.exceptionTypes;
|
||||
}
|
||||
|
||||
public void addExceptionType(String exceptionType) {
|
||||
|
||||
if (this.exceptionTypes == null) {
|
||||
return;
|
||||
}
|
||||
@@ -100,30 +76,6 @@ public class ExceptionRangeCFG {
|
||||
}
|
||||
|
||||
public String getUniqueExceptionsString() {
|
||||
|
||||
if (exceptionTypes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Set<String> setExceptionStrings = new HashSet<String>();
|
||||
|
||||
for (String exceptionType : exceptionTypes) { // normalize order
|
||||
setExceptionStrings.add(exceptionType);
|
||||
}
|
||||
|
||||
String ret = "";
|
||||
for (String exception : setExceptionStrings) {
|
||||
if (!ret.isEmpty()) {
|
||||
ret += ":";
|
||||
}
|
||||
ret += exception;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return exceptionTypes != null ? exceptionTypes.stream().distinct().collect(Collectors.joining(":")) : null;
|
||||
}
|
||||
|
||||
|
||||
// public void setExceptionType(String exceptionType) {
|
||||
// this.exceptionType = exceptionType;
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.code.interpreter;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
@@ -339,7 +325,6 @@ public class InstructionImpact {
|
||||
|
||||
|
||||
public static void stepTypes(DataPoint data, Instruction instr, ConstantPool pool) {
|
||||
|
||||
ListStack<VarType> stack = data.getStack();
|
||||
int[][] arr = stack_impact[instr.opcode];
|
||||
|
||||
@@ -351,8 +336,7 @@ public class InstructionImpact {
|
||||
|
||||
if (read != null) {
|
||||
int depth = 0;
|
||||
for (int i = 0; i < read.length; i++) {
|
||||
int type = read[i];
|
||||
for (int type : read) {
|
||||
depth++;
|
||||
if (type == CodeConstants.TYPE_LONG ||
|
||||
type == CodeConstants.TYPE_DOUBLE) {
|
||||
@@ -364,8 +348,7 @@ public class InstructionImpact {
|
||||
}
|
||||
|
||||
if (write != null) {
|
||||
for (int i = 0; i < write.length; i++) {
|
||||
int type = write[i];
|
||||
for (int type : write) {
|
||||
stack.push(new VarType(type));
|
||||
if (type == CodeConstants.TYPE_LONG ||
|
||||
type == CodeConstants.TYPE_DOUBLE) {
|
||||
@@ -395,7 +378,7 @@ public class InstructionImpact {
|
||||
case CodeConstants.opc_ldc:
|
||||
case CodeConstants.opc_ldc_w:
|
||||
case CodeConstants.opc_ldc2_w:
|
||||
PooledConstant constant = pool.getConstant(instr.getOperand(0));
|
||||
PooledConstant constant = pool.getConstant(instr.operand(0));
|
||||
switch (constant.type) {
|
||||
case CodeConstants.CONSTANT_Integer:
|
||||
stack.push(new VarType(CodeConstants.TYPE_INT));
|
||||
@@ -423,7 +406,7 @@ public class InstructionImpact {
|
||||
}
|
||||
break;
|
||||
case CodeConstants.opc_aload:
|
||||
var1 = data.getVariable(instr.getOperand(0));
|
||||
var1 = data.getVariable(instr.operand(0));
|
||||
if (var1 != null) {
|
||||
stack.push(var1);
|
||||
}
|
||||
@@ -436,7 +419,7 @@ public class InstructionImpact {
|
||||
stack.push(new VarType(var1.type, var1.arrayDim - 1, var1.value));
|
||||
break;
|
||||
case CodeConstants.opc_astore:
|
||||
data.setVariable(instr.getOperand(0), stack.pop());
|
||||
data.setVariable(instr.operand(0), stack.pop());
|
||||
break;
|
||||
case CodeConstants.opc_dup:
|
||||
case CodeConstants.opc_dup_x1:
|
||||
@@ -458,7 +441,7 @@ public class InstructionImpact {
|
||||
case CodeConstants.opc_getfield:
|
||||
stack.pop();
|
||||
case CodeConstants.opc_getstatic:
|
||||
ck = pool.getLinkConstant(instr.getOperand(0));
|
||||
ck = pool.getLinkConstant(instr.operand(0));
|
||||
var1 = new VarType(ck.descriptor);
|
||||
stack.push(var1);
|
||||
if (var1.stackSize == 2) {
|
||||
@@ -468,7 +451,7 @@ public class InstructionImpact {
|
||||
case CodeConstants.opc_putfield:
|
||||
stack.pop();
|
||||
case CodeConstants.opc_putstatic:
|
||||
ck = pool.getLinkConstant(instr.getOperand(0));
|
||||
ck = pool.getLinkConstant(instr.operand(0));
|
||||
var1 = new VarType(ck.descriptor);
|
||||
stack.pop(var1.stackSize);
|
||||
break;
|
||||
@@ -478,8 +461,8 @@ public class InstructionImpact {
|
||||
stack.pop();
|
||||
case CodeConstants.opc_invokestatic:
|
||||
case CodeConstants.opc_invokedynamic:
|
||||
if (instr.opcode != CodeConstants.opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) {
|
||||
ck = pool.getLinkConstant(instr.getOperand(0));
|
||||
if (instr.opcode != CodeConstants.opc_invokedynamic || instr.bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) {
|
||||
ck = pool.getLinkConstant(instr.operand(0));
|
||||
MethodDescriptor md = MethodDescriptor.parseDescriptor(ck.descriptor);
|
||||
for (int i = 0; i < md.params.length; i++) {
|
||||
stack.pop(md.params[i].stackSize);
|
||||
@@ -493,12 +476,12 @@ public class InstructionImpact {
|
||||
}
|
||||
break;
|
||||
case CodeConstants.opc_new:
|
||||
cn = pool.getPrimitiveConstant(instr.getOperand(0));
|
||||
cn = pool.getPrimitiveConstant(instr.operand(0));
|
||||
stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString()));
|
||||
break;
|
||||
case CodeConstants.opc_newarray:
|
||||
stack.pop();
|
||||
stack.push(new VarType(arr_type[instr.getOperand(0) - 4], 1).resizeArrayDim(1));
|
||||
stack.push(new VarType(arr_type[instr.operand(0) - 4], 1).resizeArrayDim(1));
|
||||
break;
|
||||
case CodeConstants.opc_athrow:
|
||||
var1 = stack.pop();
|
||||
@@ -508,14 +491,14 @@ public class InstructionImpact {
|
||||
case CodeConstants.opc_checkcast:
|
||||
case CodeConstants.opc_instanceof:
|
||||
stack.pop();
|
||||
cn = pool.getPrimitiveConstant(instr.getOperand(0));
|
||||
cn = pool.getPrimitiveConstant(instr.operand(0));
|
||||
stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString()));
|
||||
break;
|
||||
case CodeConstants.opc_anewarray:
|
||||
case CodeConstants.opc_multianewarray:
|
||||
int dimensions = (instr.opcode == CodeConstants.opc_anewarray) ? 1 : instr.getOperand(1);
|
||||
int dimensions = (instr.opcode == CodeConstants.opc_anewarray) ? 1 : instr.operand(1);
|
||||
stack.pop(dimensions);
|
||||
cn = pool.getPrimitiveConstant(instr.getOperand(0));
|
||||
cn = pool.getPrimitiveConstant(instr.operand(0));
|
||||
if (cn.isArray) {
|
||||
var1 = new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString());
|
||||
var1 = var1.resizeArrayDim(var1.arrayDim + dimensions);
|
||||
|
||||
@@ -1,286 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.interpreter;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
import org.jetbrains.java.decompiler.struct.StructClass;
|
||||
import org.jetbrains.java.decompiler.struct.StructContext;
|
||||
|
||||
|
||||
// FIXME: move to StructContext
|
||||
public class Util {
|
||||
|
||||
private static final String[][] runtime_exceptions = {
|
||||
|
||||
null, // public final static int opc_nop = 0;
|
||||
null, // public final static int opc_aconst_null = 1;
|
||||
null, // public final static int opc_iconst_m1 = 2;
|
||||
null, // public final static int opc_iconst_0 = 3;
|
||||
null, // public final static int opc_iconst_1 = 4;
|
||||
null, // public final static int opc_iconst_2 = 5;
|
||||
null, // public final static int opc_iconst_3 = 6;
|
||||
null, // public final static int opc_iconst_4 = 7;
|
||||
null, // public final static int opc_iconst_5 = 8;
|
||||
null, // public final static int opc_lconst_0 = 9;
|
||||
null, // public final static int opc_lconst_1 = 10;
|
||||
null, // public final static int opc_fconst_0 = 11;
|
||||
null, // public final static int opc_fconst_1 = 12;
|
||||
null, // public final static int opc_fconst_2 = 13;
|
||||
null, // public final static int opc_dconst_0 = 14;
|
||||
null, // public final static int opc_dconst_1 = 15;
|
||||
null, // public final static int opc_bipush = 16;
|
||||
null, // public final static int opc_sipush = 17;
|
||||
null, // public final static int opc_ldc = 18;
|
||||
null, // public final static int opc_ldc_w = 19;
|
||||
null, // public final static int opc_ldc2_w = 20;
|
||||
null, // public final static int opc_iload = 21;
|
||||
null, // public final static int opc_lload = 22;
|
||||
null, // public final static int opc_fload = 23;
|
||||
null, // public final static int opc_dload = 24;
|
||||
null, // public final static int opc_aload = 25;
|
||||
null, // public final static int opc_iload_0 = 26;
|
||||
null, // public final static int opc_iload_1 = 27;
|
||||
null, // public final static int opc_iload_2 = 28;
|
||||
null, // public final static int opc_iload_3 = 29;
|
||||
null, // public final static int opc_lload_0 = 30;
|
||||
null, // public final static int opc_lload_1 = 31;
|
||||
null, // public final static int opc_lload_2 = 32;
|
||||
null, // public final static int opc_lload_3 = 33;
|
||||
null, // public final static int opc_fload_0 = 34;
|
||||
null, // public final static int opc_fload_1 = 35;
|
||||
null, // public final static int opc_fload_2 = 36;
|
||||
null, // public final static int opc_fload_3 = 37;
|
||||
null, // public final static int opc_dload_0 = 38;
|
||||
null, // public final static int opc_dload_1 = 39;
|
||||
null, // public final static int opc_dload_2 = 40;
|
||||
null, // public final static int opc_dload_3 = 41;
|
||||
null, // public final static int opc_aload_0 = 42;
|
||||
null, // public final static int opc_aload_1 = 43;
|
||||
null, // public final static int opc_aload_2 = 44;
|
||||
null, // public final static int opc_aload_3 = 45;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_iaload = 46;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_laload = 47;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_faload = 48;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_daload = 49;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_aaload = 50;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_baload = 51;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_caload = 52;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_saload = 53;
|
||||
null, // public final static int opc_istore = 54;
|
||||
null, // public final static int opc_lstore = 55;
|
||||
null, // public final static int opc_fstore = 56;
|
||||
null, // public final static int opc_dstore = 57;
|
||||
null, // public final static int opc_astore = 58;
|
||||
null, // public final static int opc_istore_0 = 59;
|
||||
null, // public final static int opc_istore_1 = 60;
|
||||
null, // public final static int opc_istore_2 = 61;
|
||||
null, // public final static int opc_istore_3 = 62;
|
||||
null, // public final static int opc_lstore_0 = 63;
|
||||
null, // public final static int opc_lstore_1 = 64;
|
||||
null, // public final static int opc_lstore_2 = 65;
|
||||
null, // public final static int opc_lstore_3 = 66;
|
||||
null, // public final static int opc_fstore_0 = 67;
|
||||
null, // public final static int opc_fstore_1 = 68;
|
||||
null, // public final static int opc_fstore_2 = 69;
|
||||
null, // public final static int opc_fstore_3 = 70;
|
||||
null, // public final static int opc_dstore_0 = 71;
|
||||
null, // public final static int opc_dstore_1 = 72;
|
||||
null, // public final static int opc_dstore_2 = 73;
|
||||
null, // public final static int opc_dstore_3 = 74;
|
||||
null, // public final static int opc_astore_0 = 75;
|
||||
null, // public final static int opc_astore_1 = 76;
|
||||
null, // public final static int opc_astore_2 = 77;
|
||||
null, // public final static int opc_astore_3 = 78;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_iastore = 79;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_lastore = 80;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_fastore = 81;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_dastore = 82;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException", "java/lang/ArrayStoreException"},
|
||||
// public final static int opc_aastore = 83;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_bastore = 84;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_castore = 85;
|
||||
{"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"},
|
||||
// public final static int opc_sastore = 86;
|
||||
null, // public final static int opc_pop = 87;
|
||||
null, // public final static int opc_pop2 = 88;
|
||||
null, // public final static int opc_dup = 89;
|
||||
null, // public final static int opc_dup_x1 = 90;
|
||||
null, // public final static int opc_dup_x2 = 91;
|
||||
null, // public final static int opc_dup2 = 92;
|
||||
null, // public final static int opc_dup2_x1 = 93;
|
||||
null, // public final static int opc_dup2_x2 = 94;
|
||||
null, // public final static int opc_swap = 95;
|
||||
null, // public final static int opc_iadd = 96;
|
||||
null, // public final static int opc_ladd = 97;
|
||||
null, // public final static int opc_fadd = 98;
|
||||
null, // public final static int opc_dadd = 99;
|
||||
null, // public final static int opc_isub = 100;
|
||||
null, // public final static int opc_lsub = 101;
|
||||
null, // public final static int opc_fsub = 102;
|
||||
null, // public final static int opc_dsub = 103;
|
||||
null, // public final static int opc_imul = 104;
|
||||
null, // public final static int opc_lmul = 105;
|
||||
null, // public final static int opc_fmul = 106;
|
||||
null, // public final static int opc_dmul = 107;
|
||||
{"java/lang/ArithmeticException"}, // public final static int opc_idiv = 108;
|
||||
{"java/lang/ArithmeticException"}, // public final static int opc_ldiv = 109;
|
||||
null, // public final static int opc_fdiv = 110;
|
||||
null, // public final static int opc_ddiv = 111;
|
||||
{"java/lang/ArithmeticException"}, // public final static int opc_irem = 112;
|
||||
{"java/lang/ArithmeticException"}, // public final static int opc_lrem = 113;
|
||||
null, // public final static int opc_frem = 114;
|
||||
null, // public final static int opc_drem = 115;
|
||||
null, // public final static int opc_ineg = 116;
|
||||
null, // public final static int opc_lneg = 117;
|
||||
null, // public final static int opc_fneg = 118;
|
||||
null, // public final static int opc_dneg = 119;
|
||||
null, // public final static int opc_ishl = 120;
|
||||
null, // public final static int opc_lshl = 121;
|
||||
null, // public final static int opc_ishr = 122;
|
||||
null, // public final static int opc_lshr = 123;
|
||||
null, // public final static int opc_iushr = 124;
|
||||
null, // public final static int opc_lushr = 125;
|
||||
null, // public final static int opc_iand = 126;
|
||||
null, // public final static int opc_land = 127;
|
||||
null, // public final static int opc_ior = 128;
|
||||
null, // public final static int opc_lor = 129;
|
||||
null, // public final static int opc_ixor = 130;
|
||||
null, // public final static int opc_lxor = 131;
|
||||
null, // public final static int opc_iinc = 132;
|
||||
null, // public final static int opc_i2l = 133;
|
||||
null, // public final static int opc_i2f = 134;
|
||||
null, // public final static int opc_i2d = 135;
|
||||
null, // public final static int opc_l2i = 136;
|
||||
null, // public final static int opc_l2f = 137;
|
||||
null, // public final static int opc_l2d = 138;
|
||||
null, // public final static int opc_f2i = 139;
|
||||
null, // public final static int opc_f2l = 140;
|
||||
null, // public final static int opc_f2d = 141;
|
||||
null, // public final static int opc_d2i = 142;
|
||||
null, // public final static int opc_d2l = 143;
|
||||
null, // public final static int opc_d2f = 144;
|
||||
null, // public final static int opc_i2b = 145;
|
||||
null, // public final static int opc_i2c = 146;
|
||||
null, // public final static int opc_i2s = 147;
|
||||
null, // public final static int opc_lcmp = 148;
|
||||
null, // public final static int opc_fcmpl = 149;
|
||||
null, // public final static int opc_fcmpg = 150;
|
||||
null, // public final static int opc_dcmpl = 151;
|
||||
null, // public final static int opc_dcmpg = 152;
|
||||
null, // public final static int opc_ifeq = 153;
|
||||
null, // public final static int opc_ifne = 154;
|
||||
null, // public final static int opc_iflt = 155;
|
||||
null, // public final static int opc_ifge = 156;
|
||||
null, // public final static int opc_ifgt = 157;
|
||||
null, // public final static int opc_ifle = 158;
|
||||
null, // public final static int opc_if_icmpeq = 159;
|
||||
null, // public final static int opc_if_icmpne = 160;
|
||||
null, // public final static int opc_if_icmplt = 161;
|
||||
null, // public final static int opc_if_icmpge = 162;
|
||||
null, // public final static int opc_if_icmpgt = 163;
|
||||
null, // public final static int opc_if_icmple = 164;
|
||||
null, // public final static int opc_if_acmpeq = 165;
|
||||
null, // public final static int opc_if_acmpne = 166;
|
||||
null, // public final static int opc_goto = 167;
|
||||
null, // public final static int opc_jsr = 168;
|
||||
null, // public final static int opc_ret = 169;
|
||||
null, // public final static int opc_tableswitch = 170;
|
||||
null, // public final static int opc_lookupswitch = 171;
|
||||
{"java/lang/IllegalMonitorStateException"}, // public final static int opc_ireturn = 172;
|
||||
{"java/lang/IllegalMonitorStateException"}, // public final static int opc_lreturn = 173;
|
||||
{"java/lang/IllegalMonitorStateException"}, // public final static int opc_freturn = 174;
|
||||
{"java/lang/IllegalMonitorStateException"}, // public final static int opc_dreturn = 175;
|
||||
{"java/lang/IllegalMonitorStateException"}, // public final static int opc_areturn = 176;
|
||||
{"java/lang/IllegalMonitorStateException"}, // public final static int opc_return = 177;
|
||||
null, // public final static int opc_getstatic = 178;
|
||||
null, // public final static int opc_putstatic = 179;
|
||||
{"java/lang/NullPointerException"}, // public final static int opc_getfield = 180;
|
||||
{"java/lang/NullPointerException"}, // public final static int opc_putfield = 181;
|
||||
{"java/lang/NullPointerException", "java/lang/AbstractMethodError", "java/lang/UnsatisfiedLinkError"},
|
||||
// public final static int opc_invokevirtual = 182;
|
||||
{"java/lang/NullPointerException", "java/lang/UnsatisfiedLinkError"},
|
||||
// public final static int opc_invokespecial = 183;
|
||||
{"java/lang/UnsatisfiedLinkError"}, // public final static int opc_invokestatic = 184;
|
||||
{"java/lang/NullPointerException", "java/lang/IncompatibleClassChangeError", "java/lang/IllegalAccessError",
|
||||
"java/lang/java/lang/AbstractMethodError", "java/lang/UnsatisfiedLinkError"},
|
||||
// public final static int opc_invokeinterface = 185;
|
||||
null, // public final static int opc_xxxunusedxxx = 186;
|
||||
null, // public final static int opc_new = 187;
|
||||
{"java/lang/NegativeArraySizeException"}, // public final static int opc_newarray = 188;
|
||||
{"java/lang/NegativeArraySizeException"}, // public final static int opc_anewarray = 189;
|
||||
{"java/lang/NullPointerException"}, // public final static int opc_arraylength = 190;
|
||||
{"java/lang/NullPointerException", "java/lang/IllegalMonitorStateException"},
|
||||
// public final static int opc_athrow = 191;
|
||||
{"java/lang/ClassCastException"}, // public final static int opc_checkcast = 192;
|
||||
null, // public final static int opc_instanceof = 193;
|
||||
{"java/lang/NullPointerException"}, // public final static int opc_monitorenter = 194;
|
||||
{"java/lang/NullPointerException", "java/lang/IllegalMonitorStateException"},
|
||||
// public final static int opc_monitorexit = 195;
|
||||
null, // public final static int opc_wide = 196;
|
||||
{"java/lang/NegativeArraySizeException"}, // public final static int opc_multianewarray = 197;
|
||||
null, // public final static int opc_ifnull = 198;
|
||||
null, // public final static int opc_ifnonnull = 199;
|
||||
null, // public final static int opc_goto_w = 200;
|
||||
null, // public final static int opc_jsr_w = 201;
|
||||
};
|
||||
|
||||
|
||||
public static boolean instanceOf(StructContext context, String valclass, String refclass) {
|
||||
|
||||
if (valclass.equals(refclass)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
StructClass cl = context.getClass(valclass);
|
||||
if (cl == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cl.superClass != null && instanceOf(context, cl.superClass.getString(), refclass)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int[] interfaces = cl.getInterfaces();
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
String intfc = cl.getPool().getPrimitiveConstant(interfaces[i]).getString();
|
||||
|
||||
if (instanceOf(context, intfc, refclass)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static String[] getRuntimeExceptions(Instruction instr) {
|
||||
return runtime_exceptions[instr.opcode];
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ALOAD extends Instruction {
|
||||
|
||||
private static final int[] opcodes = new int[]{opc_aload_0, opc_aload_1, opc_aload_2, opc_aload_3};
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
out.writeByte(opc_wide);
|
||||
}
|
||||
out.writeByte(opc_aload);
|
||||
if (wide) {
|
||||
out.writeShort(index);
|
||||
}
|
||||
else {
|
||||
out.writeByte(index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.writeByte(opcodes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
return 4;
|
||||
}
|
||||
else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ANEWARRAY extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_anewarray);
|
||||
out.writeShort(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ASTORE extends Instruction {
|
||||
|
||||
private static final int[] opcodes = new int[]{opc_astore_0, opc_astore_1, opc_astore_2, opc_astore_3};
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
out.writeByte(opc_wide);
|
||||
}
|
||||
out.writeByte(opc_astore);
|
||||
if (wide) {
|
||||
out.writeShort(index);
|
||||
}
|
||||
else {
|
||||
out.writeByte(index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.writeByte(opcodes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
return 4;
|
||||
}
|
||||
else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class BIPUSH extends Instruction {
|
||||
|
||||
private static final int[] opcodes =
|
||||
new int[]{opc_iconst_m1, opc_iconst_0, opc_iconst_1, opc_iconst_2, opc_iconst_3, opc_iconst_4, opc_iconst_5};
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
int value = getOperand(0);
|
||||
if (value < -1 || value > 5) {
|
||||
out.writeByte(opc_bipush);
|
||||
out.writeByte(value);
|
||||
}
|
||||
else {
|
||||
out.writeByte(opcodes[value + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
int value = getOperand(0);
|
||||
if (value < -1 || value > 5) {
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class CHECKCAST extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_checkcast);
|
||||
out.writeShort(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class DLOAD extends Instruction {
|
||||
|
||||
private static final int[] opcodes = new int[]{opc_dload_0, opc_dload_1, opc_dload_2, opc_dload_3};
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
out.writeByte(opc_wide);
|
||||
}
|
||||
out.writeByte(opc_dload);
|
||||
if (wide) {
|
||||
out.writeShort(index);
|
||||
}
|
||||
else {
|
||||
out.writeByte(index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.writeByte(opcodes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
return 4;
|
||||
}
|
||||
else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class DSTORE extends Instruction {
|
||||
|
||||
private static final int[] opcodes = new int[]{opc_dstore_0, opc_dstore_1, opc_dstore_2, opc_dstore_3};
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
out.writeByte(opc_wide);
|
||||
}
|
||||
out.writeByte(opc_dstore);
|
||||
if (wide) {
|
||||
out.writeShort(index);
|
||||
}
|
||||
else {
|
||||
out.writeByte(index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.writeByte(opcodes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
return 4;
|
||||
}
|
||||
else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class FLOAD extends Instruction {
|
||||
|
||||
private static final int[] opcodes = new int[]{opc_fload_0, opc_fload_1, opc_fload_2, opc_fload_3};
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
out.writeByte(opc_wide);
|
||||
}
|
||||
out.writeByte(opc_fload);
|
||||
if (wide) {
|
||||
out.writeShort(index);
|
||||
}
|
||||
else {
|
||||
out.writeByte(index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.writeByte(opcodes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
return 4;
|
||||
}
|
||||
else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class FSTORE extends Instruction {
|
||||
|
||||
private static final int[] opcodes = new int[]{opc_fstore_0, opc_fstore_1, opc_fstore_2, opc_fstore_3};
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
out.writeByte(opc_wide);
|
||||
}
|
||||
out.writeByte(opc_fstore);
|
||||
if (wide) {
|
||||
out.writeShort(index);
|
||||
}
|
||||
else {
|
||||
out.writeByte(index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.writeByte(opcodes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
return 4;
|
||||
}
|
||||
else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class GETSTATIC extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_getstatic);
|
||||
out.writeShort(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.JumpInstruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class GOTO extends JumpInstruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
int operand = getOperand(0);
|
||||
if (operand < -32768 || operand > 32767) {
|
||||
out.writeByte(opc_goto_w);
|
||||
out.writeInt(operand);
|
||||
}
|
||||
else {
|
||||
out.writeByte(opc_goto);
|
||||
out.writeShort(operand);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
int operand = getOperand(0);
|
||||
if (operand < -32768 || operand > 32767) {
|
||||
return 5;
|
||||
}
|
||||
else {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.JumpInstruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class GOTO_W extends JumpInstruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_goto_w);
|
||||
out.writeInt(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class IINC extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
if (wide) {
|
||||
out.writeByte(opc_wide);
|
||||
}
|
||||
out.writeByte(opc_iinc);
|
||||
if (wide) {
|
||||
out.writeShort(getOperand(0));
|
||||
out.writeShort(getOperand(1));
|
||||
}
|
||||
else {
|
||||
out.writeByte(getOperand(0));
|
||||
out.writeByte(getOperand(1));
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return wide ? 6 : 3;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ILOAD extends Instruction {
|
||||
|
||||
private static final int[] opcodes = new int[]{opc_iload_0, opc_iload_1, opc_iload_2, opc_iload_3};
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
out.writeByte(opc_wide);
|
||||
}
|
||||
out.writeByte(opc_iload);
|
||||
if (wide) {
|
||||
out.writeShort(index);
|
||||
}
|
||||
else {
|
||||
out.writeByte(index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.writeByte(opcodes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
return wide ? 4 : 2;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class INSTANCEOF extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_instanceof);
|
||||
out.writeShort(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class INVOKEDYNAMIC extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_invokedynamic);
|
||||
out.writeShort(getOperand(0));
|
||||
out.writeByte(0);
|
||||
out.writeByte(0);
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class INVOKEINTERFACE extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_invokeinterface);
|
||||
out.writeShort(getOperand(0));
|
||||
out.writeByte(getOperand(1));
|
||||
out.writeByte(0);
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class INVOKESPECIAL extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_invokespecial);
|
||||
out.writeShort(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class INVOKESTATIC extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_invokestatic);
|
||||
out.writeShort(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class INVOKEVIRTUAL extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_invokevirtual);
|
||||
out.writeShort(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ISTORE extends Instruction {
|
||||
|
||||
private static final int[] opcodes = new int[]{opc_istore_0, opc_istore_1, opc_istore_2, opc_istore_3};
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
out.writeByte(opc_wide);
|
||||
}
|
||||
out.writeByte(opc_istore);
|
||||
if (wide) {
|
||||
out.writeShort(index);
|
||||
}
|
||||
else {
|
||||
out.writeByte(index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.writeByte(opcodes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
return wide ? 4 : 2;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.JumpInstruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class JSR extends JumpInstruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
int operand = getOperand(0);
|
||||
if (operand < -32768 || operand > 32767) {
|
||||
out.writeByte(opc_jsr_w);
|
||||
out.writeInt(operand);
|
||||
}
|
||||
else {
|
||||
out.writeByte(opc_jsr);
|
||||
out.writeShort(operand);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
int operand = getOperand(0);
|
||||
if (operand < -32768 || operand > 32767) {
|
||||
return 5;
|
||||
}
|
||||
else {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.JumpInstruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class JSR_W extends JumpInstruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_jsr_w);
|
||||
out.writeInt(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LLOAD extends Instruction {
|
||||
|
||||
private static final int[] opcodes = new int[]{opc_lload_0, opc_lload_1, opc_lload_2, opc_lload_3};
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
out.writeByte(opc_wide);
|
||||
}
|
||||
out.writeByte(opc_lload);
|
||||
if (wide) {
|
||||
out.writeShort(index);
|
||||
}
|
||||
else {
|
||||
out.writeByte(index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.writeByte(opcodes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
return wide ? 4 : 2;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.SwitchInstruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LOOKUPSWITCH extends SwitchInstruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
|
||||
out.writeByte(opc_lookupswitch);
|
||||
|
||||
int padding = 3 - (offset % 4);
|
||||
for (int i = 0; i < padding; i++) {
|
||||
out.writeByte(0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < operandsCount(); i++) {
|
||||
out.writeInt(getOperand(i));
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 1 + operandsCount() * 4;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LSTORE extends Instruction {
|
||||
|
||||
private static final int[] opcodes = new int[]{opc_lstore_0, opc_lstore_1, opc_lstore_2, opc_lstore_3};
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
if (wide) {
|
||||
out.writeByte(opc_wide);
|
||||
}
|
||||
out.writeByte(opc_lstore);
|
||||
if (wide) {
|
||||
out.writeShort(index);
|
||||
}
|
||||
else {
|
||||
out.writeByte(index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.writeByte(opcodes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
int index = getOperand(0);
|
||||
if (index > 3) {
|
||||
return wide ? 4 : 2;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MULTIANEWARRAY extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_multianewarray);
|
||||
out.writeShort(getOperand(0));
|
||||
out.writeByte(getOperand(1));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class NEWARRAY extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_newarray);
|
||||
out.writeByte(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PUTFIELD extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_putfield);
|
||||
out.writeShort(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PUTSTATIC extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_putstatic);
|
||||
out.writeShort(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class RET extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
if (wide) {
|
||||
out.writeByte(opc_wide);
|
||||
}
|
||||
out.writeByte(opc_ret);
|
||||
if (wide) {
|
||||
out.writeShort(getOperand(0));
|
||||
}
|
||||
else {
|
||||
out.writeByte(getOperand(0));
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return wide ? 4 : 2;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class SIPUSH extends Instruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
out.writeByte(opc_sipush);
|
||||
out.writeShort(getOperand(0));
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.code.optinstructions;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.SwitchInstruction;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class TABLESWITCH extends SwitchInstruction {
|
||||
|
||||
public void writeToStream(DataOutputStream out, int offset) throws IOException {
|
||||
|
||||
out.writeByte(opc_tableswitch);
|
||||
|
||||
int padding = 3 - (offset % 4);
|
||||
for (int i = 0; i < padding; i++) {
|
||||
out.writeByte(0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < operandsCount(); i++) {
|
||||
out.writeInt(getOperand(i));
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 1 + operandsCount() * 4;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
@@ -157,25 +143,42 @@ public class AssertProcessor {
|
||||
|
||||
private static boolean replaceAssertion(Statement parent, IfStatement stat, String classname, String key) {
|
||||
|
||||
boolean throwInIf = true;
|
||||
Statement ifstat = stat.getIfstat();
|
||||
InvocationExprent throwError = isAssertionError(ifstat);
|
||||
|
||||
if (throwError == null) {
|
||||
return false;
|
||||
//check else:
|
||||
Statement elsestat = stat.getElsestat();
|
||||
throwError = isAssertionError(elsestat);
|
||||
if (throwError == null) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
throwInIf = false;
|
||||
}
|
||||
}
|
||||
|
||||
Object[] exprres = getAssertionExprent(stat.getHeadexprent().getCondition().copy(), classname, key);
|
||||
|
||||
Object[] exprres = getAssertionExprent(stat.getHeadexprent().getCondition().copy(), classname, key, throwInIf);
|
||||
if (!(Boolean)exprres[1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Exprent> lstParams = new ArrayList<Exprent>();
|
||||
List<Exprent> lstParams = new ArrayList<>();
|
||||
|
||||
Exprent ascond = null, retcond = null;
|
||||
if (exprres[0] != null) {
|
||||
ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, (Exprent)exprres[0], throwError.bytecode);
|
||||
retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond);
|
||||
if (throwInIf) {
|
||||
if (exprres[0] != null) {
|
||||
ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, (Exprent)exprres[0], throwError.bytecode);
|
||||
retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ascond = (Exprent) exprres[0];
|
||||
retcond = ascond;
|
||||
}
|
||||
|
||||
|
||||
lstParams.add(retcond == null ? ascond : retcond);
|
||||
if (!throwError.getLstParameters().isEmpty()) {
|
||||
@@ -196,13 +199,18 @@ public class AssertProcessor {
|
||||
first.removeSuccessor(stat.getIfEdge());
|
||||
first.removeSuccessor(stat.getElseEdge());
|
||||
|
||||
List<Statement> lstStatements = new ArrayList<Statement>();
|
||||
List<Statement> lstStatements = new ArrayList<>();
|
||||
if (first.getExprents() != null && !first.getExprents().isEmpty()) {
|
||||
lstStatements.add(first);
|
||||
}
|
||||
lstStatements.add(newstat);
|
||||
if (stat.iftype == IfStatement.IFTYPE_IFELSE) {
|
||||
lstStatements.add(stat.getElsestat());
|
||||
if (throwInIf) {
|
||||
lstStatements.add(stat.getElsestat());
|
||||
}
|
||||
else {
|
||||
lstStatements.add(stat.getIfstat());
|
||||
}
|
||||
}
|
||||
|
||||
SequenceStatement sequence = new SequenceStatement(lstStatements);
|
||||
@@ -213,10 +221,16 @@ public class AssertProcessor {
|
||||
sequence.getStats().get(i), sequence.getStats().get(i + 1)));
|
||||
}
|
||||
|
||||
if (stat.iftype == IfStatement.IFTYPE_IFELSE) {
|
||||
Statement ifelse = stat.getElsestat();
|
||||
if (stat.iftype == IfStatement.IFTYPE_IFELSE || !throwInIf) {
|
||||
Statement stmts;
|
||||
if (throwInIf) {
|
||||
stmts = stat.getElsestat();
|
||||
}
|
||||
else {
|
||||
stmts = stat.getIfstat();
|
||||
}
|
||||
|
||||
List<StatEdge> lstSuccs = ifelse.getAllSuccessorEdges();
|
||||
List<StatEdge> lstSuccs = stmts.getAllSuccessorEdges();
|
||||
if (!lstSuccs.isEmpty()) {
|
||||
StatEdge endedge = lstSuccs.get(0);
|
||||
if (endedge.closure == stat) {
|
||||
@@ -255,16 +269,21 @@ public class AssertProcessor {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Object[] getAssertionExprent(Exprent exprent, String classname, String key) {
|
||||
private static Object[] getAssertionExprent(Exprent exprent, String classname, String key, boolean throwInIf) {
|
||||
|
||||
if (exprent.type == Exprent.EXPRENT_FUNCTION) {
|
||||
int desiredOperation = FunctionExprent.FUNCTION_CADD;
|
||||
if (!throwInIf) {
|
||||
desiredOperation = FunctionExprent.FUNCTION_COR;
|
||||
}
|
||||
|
||||
FunctionExprent fexpr = (FunctionExprent)exprent;
|
||||
if (fexpr.getFuncType() == FunctionExprent.FUNCTION_CADD) {
|
||||
if (fexpr.getFuncType() == desiredOperation) {
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Exprent param = fexpr.getLstOperands().get(i);
|
||||
|
||||
if (isAssertionField(param, classname, key)) {
|
||||
if (isAssertionField(param, classname, key, throwInIf)) {
|
||||
return new Object[]{fexpr.getLstOperands().get(1 - i), true};
|
||||
}
|
||||
}
|
||||
@@ -272,7 +291,7 @@ public class AssertProcessor {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Exprent param = fexpr.getLstOperands().get(i);
|
||||
|
||||
Object[] res = getAssertionExprent(param, classname, key);
|
||||
Object[] res = getAssertionExprent(param, classname, key, throwInIf);
|
||||
if ((Boolean)res[1]) {
|
||||
if (param != res[0]) {
|
||||
fexpr.getLstOperands().set(i, (Exprent)res[0]);
|
||||
@@ -281,7 +300,7 @@ public class AssertProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isAssertionField(fexpr, classname, key)) {
|
||||
else if (isAssertionField(fexpr, classname, key, throwInIf)) {
|
||||
// assert false;
|
||||
return new Object[]{null, true};
|
||||
}
|
||||
@@ -290,20 +309,26 @@ public class AssertProcessor {
|
||||
return new Object[]{exprent, false};
|
||||
}
|
||||
|
||||
private static boolean isAssertionField(Exprent exprent, String classname, String key) {
|
||||
|
||||
if (exprent.type == Exprent.EXPRENT_FUNCTION) {
|
||||
FunctionExprent fparam = (FunctionExprent)exprent;
|
||||
if (fparam.getFuncType() == FunctionExprent.FUNCTION_BOOL_NOT &&
|
||||
fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) {
|
||||
FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0);
|
||||
if (classname.equals(fdparam.getClassname())
|
||||
&& key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString))) {
|
||||
return true;
|
||||
private static boolean isAssertionField(Exprent exprent, String classname, String key, boolean throwInIf) {
|
||||
if (throwInIf) {
|
||||
if (exprent.type == Exprent.EXPRENT_FUNCTION) {
|
||||
FunctionExprent fparam = (FunctionExprent)exprent;
|
||||
if (fparam.getFuncType() == FunctionExprent.FUNCTION_BOOL_NOT &&
|
||||
fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) {
|
||||
FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0);
|
||||
return classname.equals(fdparam.getClassname()) &&
|
||||
key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if (exprent.type == Exprent.EXPRENT_FIELD) {
|
||||
FieldExprent fdparam = (FieldExprent) exprent;
|
||||
return classname.equals(fdparam.getClassname()) &&
|
||||
key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
@@ -37,78 +23,47 @@ import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class ClassReference14Processor {
|
||||
private static final ExitExprent BODY_EXPR;
|
||||
private static final ExitExprent HANDLER_EXPR;
|
||||
|
||||
public final ExitExprent bodyexprent;
|
||||
static {
|
||||
InvocationExprent invFor = new InvocationExprent();
|
||||
invFor.setName("forName");
|
||||
invFor.setClassname("java/lang/Class");
|
||||
invFor.setStringDescriptor("(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
invFor.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/String;)Ljava/lang/Class;"));
|
||||
invFor.setStatic(true);
|
||||
invFor.setLstParameters(Collections.singletonList(new VarExprent(0, VarType.VARTYPE_STRING, null)));
|
||||
BODY_EXPR = new ExitExprent(ExitExprent.EXIT_RETURN, invFor, VarType.VARTYPE_CLASS, null);
|
||||
|
||||
public final ExitExprent handlerexprent;
|
||||
|
||||
|
||||
public ClassReference14Processor() {
|
||||
|
||||
InvocationExprent invfor = new InvocationExprent();
|
||||
invfor.setName("forName");
|
||||
invfor.setClassname("java/lang/Class");
|
||||
invfor.setStringDescriptor("(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
invfor.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/String;)Ljava/lang/Class;"));
|
||||
invfor.setStatic(true);
|
||||
invfor.setLstParameters(Arrays.asList(new Exprent[]{new VarExprent(0, VarType.VARTYPE_STRING, null)}));
|
||||
|
||||
bodyexprent = new ExitExprent(ExitExprent.EXIT_RETURN,
|
||||
invfor,
|
||||
VarType.VARTYPE_CLASS, null);
|
||||
|
||||
InvocationExprent constr = new InvocationExprent();
|
||||
constr.setName(CodeConstants.INIT_NAME);
|
||||
constr.setClassname("java/lang/NoClassDefFoundError");
|
||||
constr.setStringDescriptor("()V");
|
||||
constr.setFunctype(InvocationExprent.TYP_INIT);
|
||||
constr.setDescriptor(MethodDescriptor.parseDescriptor("()V"));
|
||||
|
||||
NewExprent newexpr =
|
||||
new NewExprent(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/NoClassDefFoundError"), new ArrayList<Exprent>(), null);
|
||||
newexpr.setConstructor(constr);
|
||||
|
||||
InvocationExprent invcause = new InvocationExprent();
|
||||
invcause.setName("initCause");
|
||||
invcause.setClassname("java/lang/NoClassDefFoundError");
|
||||
invcause.setStringDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
|
||||
invcause.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;"));
|
||||
invcause.setInstance(newexpr);
|
||||
invcause.setLstParameters(
|
||||
Arrays.asList(new Exprent[]{new VarExprent(2, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"), null)}));
|
||||
|
||||
handlerexprent = new ExitExprent(ExitExprent.EXIT_THROW,
|
||||
invcause,
|
||||
null, null);
|
||||
InvocationExprent ctor = new InvocationExprent();
|
||||
ctor.setName(CodeConstants.INIT_NAME);
|
||||
ctor.setClassname("java/lang/NoClassDefFoundError");
|
||||
ctor.setStringDescriptor("()V");
|
||||
ctor.setFunctype(InvocationExprent.TYP_INIT);
|
||||
ctor.setDescriptor(MethodDescriptor.parseDescriptor("()V"));
|
||||
NewExprent newExpr = new NewExprent(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/NoClassDefFoundError"), new ArrayList<>(), null);
|
||||
newExpr.setConstructor(ctor);
|
||||
InvocationExprent invCause = new InvocationExprent();
|
||||
invCause.setName("initCause");
|
||||
invCause.setClassname("java/lang/NoClassDefFoundError");
|
||||
invCause.setStringDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
|
||||
invCause.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;"));
|
||||
invCause.setInstance(newExpr);
|
||||
invCause.setLstParameters(
|
||||
Collections.singletonList(new VarExprent(2, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"), null)));
|
||||
HANDLER_EXPR = new ExitExprent(ExitExprent.EXIT_THROW, invCause, null, null);
|
||||
}
|
||||
|
||||
|
||||
public void processClassReferences(ClassNode node) {
|
||||
|
||||
ClassWrapper wrapper = node.getWrapper();
|
||||
|
||||
// int major_version = wrapper.getClassStruct().major_version;
|
||||
// int minor_version = wrapper.getClassStruct().minor_version;
|
||||
//
|
||||
// if(major_version > 48 || (major_version == 48 && minor_version > 0)) {
|
||||
// // version 1.5 or above
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (wrapper.getClassStruct().isVersionGE_1_5()) {
|
||||
// version 1.5 or above
|
||||
return;
|
||||
}
|
||||
|
||||
public static void processClassReferences(ClassNode node) {
|
||||
// find the synthetic method Class class$(String) if present
|
||||
HashMap<ClassWrapper, MethodWrapper> mapClassMeths = new HashMap<ClassWrapper, MethodWrapper>();
|
||||
Map<ClassWrapper, MethodWrapper> mapClassMeths = new HashMap<>();
|
||||
mapClassMethods(node, mapClassMeths);
|
||||
|
||||
if (mapClassMeths.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
HashSet<ClassWrapper> setFound = new HashSet<ClassWrapper>();
|
||||
Set<ClassWrapper> setFound = new HashSet<>();
|
||||
processClassRec(node, mapClassMeths, setFound);
|
||||
|
||||
if (!setFound.isEmpty()) {
|
||||
@@ -119,29 +74,21 @@ public class ClassReference14Processor {
|
||||
}
|
||||
}
|
||||
|
||||
private static void processClassRec(ClassNode node,
|
||||
final HashMap<ClassWrapper, MethodWrapper> mapClassMeths,
|
||||
final HashSet<ClassWrapper> setFound) {
|
||||
|
||||
final ClassWrapper wrapper = node.getWrapper();
|
||||
private static void processClassRec(ClassNode node, Map<ClassWrapper, MethodWrapper> mapClassMeths, Set<? super ClassWrapper> setFound) {
|
||||
ClassWrapper wrapper = node.getWrapper();
|
||||
|
||||
// search code
|
||||
for (MethodWrapper meth : wrapper.getMethods()) {
|
||||
|
||||
RootStatement root = meth.root;
|
||||
if (root != null) {
|
||||
|
||||
DirectGraph graph = meth.getOrBuildGraph();
|
||||
|
||||
graph.iterateExprents(new DirectGraph.ExprentIterator() {
|
||||
public int processExprent(Exprent exprent) {
|
||||
for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) {
|
||||
if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) {
|
||||
setFound.add(ent.getKey());
|
||||
}
|
||||
graph.iterateExprents(exprent -> {
|
||||
for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) {
|
||||
if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) {
|
||||
setFound.add(ent.getKey());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -173,7 +120,7 @@ public class ClassReference14Processor {
|
||||
}
|
||||
}
|
||||
|
||||
private void mapClassMethods(ClassNode node, Map<ClassWrapper, MethodWrapper> map) {
|
||||
private static void mapClassMethods(ClassNode node, Map<ClassWrapper, MethodWrapper> map) {
|
||||
boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
|
||||
|
||||
ClassWrapper wrapper = node.getWrapper();
|
||||
@@ -196,8 +143,8 @@ public class ClassReference14Processor {
|
||||
BasicBlockStatement handler = (BasicBlockStatement)cst.getStats().get(1);
|
||||
|
||||
if (body.getExprents().size() == 1 && handler.getExprents().size() == 1) {
|
||||
if (bodyexprent.equals(body.getExprents().get(0)) &&
|
||||
handlerexprent.equals(handler.getExprents().get(0))) {
|
||||
if (BODY_EXPR.equals(body.getExprents().get(0)) &&
|
||||
HANDLER_EXPR.equals(handler.getExprents().get(0))) {
|
||||
map.put(wrapper, method);
|
||||
break;
|
||||
}
|
||||
@@ -213,13 +160,10 @@ public class ClassReference14Processor {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean replaceInvocations(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) {
|
||||
|
||||
boolean res = false;
|
||||
|
||||
while (true) {
|
||||
|
||||
boolean found = false;
|
||||
|
||||
for (Exprent expr : exprent.getAllExprents()) {
|
||||
@@ -242,9 +186,7 @@ public class ClassReference14Processor {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
private static String isClass14Invocation(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) {
|
||||
|
||||
if (exprent.type == Exprent.EXPRENT_FUNCTION) {
|
||||
FunctionExprent fexpr = (FunctionExprent)exprent;
|
||||
if (fexpr.getFuncType() == FunctionExprent.FUNCTION_IIF) {
|
||||
@@ -293,4 +235,4 @@ public class ClassReference14Processor {
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
@@ -23,10 +9,7 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
|
||||
import org.jetbrains.java.decompiler.main.rels.ClassWrapper;
|
||||
import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
|
||||
@@ -42,27 +25,27 @@ import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.struct.gen.generics.*;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
import org.jetbrains.java.decompiler.util.TextBuffer;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ClassWriter {
|
||||
|
||||
private final ClassReference14Processor ref14processor;
|
||||
private final PoolInterceptor interceptor;
|
||||
|
||||
public ClassWriter() {
|
||||
ref14processor = new ClassReference14Processor();
|
||||
interceptor = DecompilerContext.getPoolInterceptor();
|
||||
}
|
||||
|
||||
private void invokeProcessors(ClassNode node) {
|
||||
private static void invokeProcessors(ClassNode node) {
|
||||
ClassWrapper wrapper = node.getWrapper();
|
||||
StructClass cl = wrapper.getClassStruct();
|
||||
|
||||
InitializerProcessor.extractInitializers(wrapper);
|
||||
|
||||
if (node.type == ClassNode.CLASS_ROOT && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) {
|
||||
ref14processor.processClassReferences(node);
|
||||
if (node.type == ClassNode.CLASS_ROOT &&
|
||||
!cl.isVersionGE_1_5() &&
|
||||
DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) {
|
||||
ClassReference14Processor.processClassReferences(node);
|
||||
}
|
||||
|
||||
if (cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM)) {
|
||||
@@ -99,11 +82,11 @@ public class ClassWriter {
|
||||
}
|
||||
else {
|
||||
// reference to a static method
|
||||
buffer.append(ExprProcessor.getCastTypeName(new VarType(node.lambdaInformation.content_class_name, false)));
|
||||
buffer.append(ExprProcessor.getCastTypeName(new VarType(node.lambdaInformation.content_class_name, true)));
|
||||
}
|
||||
|
||||
buffer.append("::");
|
||||
buffer.append(node.lambdaInformation.content_method_name);
|
||||
buffer.append("::")
|
||||
.append(CodeConstants.INIT_NAME.equals(node.lambdaInformation.content_method_name) ? "new" : node.lambdaInformation.content_method_name);
|
||||
}
|
||||
else {
|
||||
// lambda method
|
||||
@@ -154,15 +137,6 @@ public class ClassWriter {
|
||||
DecompilerContext.getLogger().endWriteClass();
|
||||
}
|
||||
|
||||
private static void addTracer(StructClass cls, StructMethod method, BytecodeMappingTracer tracer) {
|
||||
StructLineNumberTableAttribute lineNumberTable =
|
||||
(StructLineNumberTableAttribute)method.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE);
|
||||
tracer.setLineNumberTable(lineNumberTable);
|
||||
DecompilerContext.getBytecodeSourceMapper().addTracer(cls.qualifiedName,
|
||||
InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor()),
|
||||
tracer);
|
||||
}
|
||||
|
||||
public void classToJava(ClassNode node, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) {
|
||||
ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);
|
||||
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node);
|
||||
@@ -183,12 +157,7 @@ public class ClassWriter {
|
||||
int start_class_def = buffer.length();
|
||||
writeClassDefinition(node, buffer, indent);
|
||||
|
||||
// // count lines in class definition the easiest way
|
||||
// startLine = buffer.substring(start_class_def).toString().split(lineSeparator, -1).length - 1;
|
||||
|
||||
boolean hasContent = false;
|
||||
|
||||
// fields
|
||||
boolean enumFields = false;
|
||||
|
||||
dummy_tracer.incrementCurrentSourceLine(buffer.countLines(start_class_def));
|
||||
@@ -257,7 +226,7 @@ public class ClassWriter {
|
||||
for (ClassNode inner : node.nested) {
|
||||
if (inner.type == ClassNode.CLASS_MEMBER) {
|
||||
StructClass innerCl = inner.classStruct;
|
||||
boolean isSynthetic = (inner.access & CodeConstants.ACC_SYNTHETIC) != 0 || innerCl.isSynthetic() || inner.namelessConstructorStub;
|
||||
boolean isSynthetic = (inner.access & CodeConstants.ACC_SYNTHETIC) != 0 || innerCl.isSynthetic();
|
||||
boolean hide = isSynthetic && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) ||
|
||||
wrapper.getHiddenMembers().contains(innerCl.qualifiedName);
|
||||
if (hide) continue;
|
||||
@@ -287,6 +256,13 @@ public class ClassWriter {
|
||||
DecompilerContext.getLogger().endWriteClass();
|
||||
}
|
||||
|
||||
private static void addTracer(StructClass cls, StructMethod method, BytecodeMappingTracer tracer) {
|
||||
StructLineNumberTableAttribute table = method.getAttribute(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE);
|
||||
tracer.setLineNumberTable(table);
|
||||
String key = InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor());
|
||||
DecompilerContext.getBytecodeSourceMapper().addTracer(cls.qualifiedName, key, tracer);
|
||||
}
|
||||
|
||||
private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent) {
|
||||
if (node.type == ClassNode.CLASS_ANONYMOUS) {
|
||||
buffer.append(" {").appendLineSeparator();
|
||||
@@ -297,8 +273,8 @@ public class ClassWriter {
|
||||
StructClass cl = wrapper.getClassStruct();
|
||||
|
||||
int flags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access;
|
||||
boolean isDeprecated = cl.getAttributes().containsKey("Deprecated");
|
||||
boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic");
|
||||
boolean isDeprecated = cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED);
|
||||
boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC);
|
||||
boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0;
|
||||
boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0;
|
||||
boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0;
|
||||
@@ -316,7 +292,7 @@ public class ClassWriter {
|
||||
appendComment(buffer, "synthetic class", indent);
|
||||
}
|
||||
|
||||
appendAnnotations(buffer, cl, indent);
|
||||
appendAnnotations(buffer, indent, cl, -1);
|
||||
|
||||
buffer.appendIndent(indent);
|
||||
|
||||
@@ -341,16 +317,9 @@ public class ClassWriter {
|
||||
buffer.append("class ");
|
||||
}
|
||||
|
||||
GenericClassDescriptor descriptor = null;
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
|
||||
StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)cl.getAttributes().getWithKey("Signature");
|
||||
if (attr != null) {
|
||||
descriptor = GenericMain.parseClassSignature(attr.getSignature());
|
||||
}
|
||||
}
|
||||
|
||||
buffer.append(node.simpleName);
|
||||
|
||||
GenericClassDescriptor descriptor = getGenericClassDescriptor(cl);
|
||||
if (descriptor != null && !descriptor.fparameters.isEmpty()) {
|
||||
appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds);
|
||||
}
|
||||
@@ -396,7 +365,7 @@ public class ClassWriter {
|
||||
private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) {
|
||||
int start = buffer.length();
|
||||
boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE);
|
||||
boolean isDeprecated = fd.getAttributes().containsKey("Deprecated");
|
||||
boolean isDeprecated = fd.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED);
|
||||
boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
|
||||
|
||||
if (isDeprecated) {
|
||||
@@ -412,7 +381,7 @@ public class ClassWriter {
|
||||
appendComment(buffer, "synthetic field", indent);
|
||||
}
|
||||
|
||||
appendAnnotations(buffer, fd, indent);
|
||||
appendAnnotations(buffer, indent, fd, TypeAnnotation.FIELD);
|
||||
|
||||
buffer.appendIndent(indent);
|
||||
|
||||
@@ -424,7 +393,7 @@ public class ClassWriter {
|
||||
|
||||
GenericFieldDescriptor descriptor = null;
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
|
||||
StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)fd.getAttributes().getWithKey("Signature");
|
||||
StructGenericSignatureAttribute attr = fd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE);
|
||||
if (attr != null) {
|
||||
descriptor = GenericMain.parseFieldSignature(attr.getSignature());
|
||||
}
|
||||
@@ -453,9 +422,9 @@ public class ClassWriter {
|
||||
}
|
||||
if (initializer != null) {
|
||||
if (isEnum && initializer.type == Exprent.EXPRENT_NEW) {
|
||||
NewExprent nexpr = (NewExprent)initializer;
|
||||
nexpr.setEnumConst(true);
|
||||
buffer.append(nexpr.toJava(indent, tracer));
|
||||
NewExprent expr = (NewExprent)initializer;
|
||||
expr.setEnumConst(true);
|
||||
buffer.append(expr.toJava(indent, tracer));
|
||||
}
|
||||
else {
|
||||
buffer.append(" = ");
|
||||
@@ -464,13 +433,12 @@ public class ClassWriter {
|
||||
((ConstExprent) initializer).adjustConstType(fieldType);
|
||||
}
|
||||
|
||||
// FIXME: special case field initializer. Can map to more than one method (constructor) and bytecode intruction.
|
||||
// FIXME: special case field initializer. Can map to more than one method (constructor) and bytecode instruction
|
||||
buffer.append(initializer.toJava(indent, tracer));
|
||||
}
|
||||
}
|
||||
else if (fd.hasModifier(CodeConstants.ACC_FINAL) && fd.hasModifier(CodeConstants.ACC_STATIC)) {
|
||||
StructConstantValueAttribute attr =
|
||||
(StructConstantValueAttribute)fd.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_CONSTANT_VALUE);
|
||||
StructConstantValueAttribute attr = fd.getAttribute(StructGeneralAttribute.ATTRIBUTE_CONSTANT_VALUE);
|
||||
if (attr != null) {
|
||||
PrimitiveConstant constant = cl.getPool().getPrimitiveConstant(attr.getIndex());
|
||||
buffer.append(" = ");
|
||||
@@ -545,8 +513,9 @@ public class ClassWriter {
|
||||
try {
|
||||
buffer.append(root.toJava(indent, tracer));
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex);
|
||||
catch (Throwable t) {
|
||||
String message = "Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.";
|
||||
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t);
|
||||
methodWrapper.decompiledWithErrors = true;
|
||||
}
|
||||
}
|
||||
@@ -572,7 +541,7 @@ public class ClassWriter {
|
||||
}
|
||||
}
|
||||
|
||||
public static String toValidJavaIdentifier(String name) {
|
||||
private static String toValidJavaIdentifier(String name) {
|
||||
if (name == null || name.isEmpty()) return name;
|
||||
|
||||
boolean changed = false;
|
||||
@@ -584,7 +553,9 @@ public class ClassWriter {
|
||||
changed = true;
|
||||
res.append("_");
|
||||
}
|
||||
else res.append(c);
|
||||
else {
|
||||
res.append(c);
|
||||
}
|
||||
}
|
||||
if (!changed) {
|
||||
return name;
|
||||
@@ -607,7 +578,7 @@ public class ClassWriter {
|
||||
boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE);
|
||||
boolean isAnnotation = cl.hasModifier(CodeConstants.ACC_ANNOTATION);
|
||||
boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
|
||||
boolean isDeprecated = mt.getAttributes().containsKey("Deprecated");
|
||||
boolean isDeprecated = mt.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED);
|
||||
boolean clinit = false, init = false, dinit = false;
|
||||
|
||||
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
|
||||
@@ -629,7 +600,7 @@ public class ClassWriter {
|
||||
appendRenameComment(buffer, oldName, MType.METHOD, indent);
|
||||
}
|
||||
|
||||
boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic");
|
||||
boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC);
|
||||
boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0;
|
||||
if (isSynthetic) {
|
||||
appendComment(buffer, "synthetic method", indent);
|
||||
@@ -638,13 +609,13 @@ public class ClassWriter {
|
||||
appendComment(buffer, "bridge method", indent);
|
||||
}
|
||||
|
||||
appendAnnotations(buffer, mt, indent);
|
||||
appendAnnotations(buffer, indent, mt, TypeAnnotation.METHOD_RETURN_TYPE);
|
||||
|
||||
buffer.appendIndent(indent);
|
||||
|
||||
appendModifiers(buffer, flags, METHOD_ALLOWED, isInterface, METHOD_EXCLUDED);
|
||||
|
||||
if (isInterface && mt.containsCode()) {
|
||||
if (isInterface && !mt.hasModifier(CodeConstants.ACC_STATIC) && mt.containsCode()) {
|
||||
// 'default' modifier (Java 8)
|
||||
buffer.append("default ");
|
||||
}
|
||||
@@ -667,22 +638,19 @@ public class ClassWriter {
|
||||
|
||||
GenericMethodDescriptor descriptor = null;
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
|
||||
StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)mt.getAttributes().getWithKey("Signature");
|
||||
StructGenericSignatureAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE);
|
||||
if (attr != null) {
|
||||
descriptor = GenericMain.parseMethodSignature(attr.getSignature());
|
||||
if (descriptor != null) {
|
||||
int actualParams = md.params.length;
|
||||
List<VarVersionPair> sigFields = methodWrapper.signatureFields;
|
||||
if (sigFields != null) {
|
||||
actualParams = 0;
|
||||
for (VarVersionPair field : methodWrapper.signatureFields) {
|
||||
if (field == null) {
|
||||
actualParams++;
|
||||
}
|
||||
}
|
||||
long actualParams = md.params.length;
|
||||
List<VarVersionPair> mask = methodWrapper.synthParameters;
|
||||
if (mask != null) {
|
||||
actualParams = mask.stream().filter(Objects::isNull).count();
|
||||
}
|
||||
else if (isEnum && init) actualParams -= 2;
|
||||
if (actualParams != descriptor.params.size()) {
|
||||
else if (isEnum && init) {
|
||||
actualParams -= 2;
|
||||
}
|
||||
if (actualParams != descriptor.parameterTypes.size()) {
|
||||
String message = "Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor() + " in " + cl.qualifiedName;
|
||||
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
|
||||
descriptor = null;
|
||||
@@ -697,14 +665,14 @@ public class ClassWriter {
|
||||
if (!clinit && !dinit) {
|
||||
boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC);
|
||||
|
||||
if (descriptor != null && !descriptor.fparameters.isEmpty()) {
|
||||
appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds);
|
||||
if (descriptor != null && !descriptor.typeParameters.isEmpty()) {
|
||||
appendTypeParameters(buffer, descriptor.typeParameters, descriptor.typeParameterBounds);
|
||||
buffer.append(' ');
|
||||
}
|
||||
|
||||
if (!init) {
|
||||
if (descriptor != null) {
|
||||
buffer.append(GenericMain.getGenericCastTypeName(descriptor.ret));
|
||||
buffer.append(GenericMain.getGenericCastTypeName(descriptor.returnType));
|
||||
}
|
||||
else {
|
||||
buffer.append(ExprProcessor.getCastTypeName(md.ret));
|
||||
@@ -715,79 +683,80 @@ public class ClassWriter {
|
||||
buffer.append(toValidJavaIdentifier(name));
|
||||
buffer.append('(');
|
||||
|
||||
// parameters
|
||||
List<VarVersionPair> signFields = methodWrapper.signatureFields;
|
||||
List<VarVersionPair> mask = methodWrapper.synthParameters;
|
||||
|
||||
int lastVisibleParameterIndex = -1;
|
||||
for (int i = 0; i < md.params.length; i++) {
|
||||
if (signFields == null || signFields.get(i) == null) {
|
||||
if (mask == null || mask.get(i) == null) {
|
||||
lastVisibleParameterIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
boolean firstParameter = true;
|
||||
List<StructMethodParametersAttribute.Entry> methodParameters = null;
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.USE_METHOD_PARAMETERS)) {
|
||||
StructMethodParametersAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_METHOD_PARAMETERS);
|
||||
if (attr != null) {
|
||||
methodParameters = attr.getEntries();
|
||||
}
|
||||
}
|
||||
|
||||
int index = isEnum && init ? 3 : thisVar ? 1 : 0;
|
||||
boolean hasDescriptor = descriptor != null;
|
||||
int start = isEnum && init && !hasDescriptor ? 2 : 0;
|
||||
int params = hasDescriptor ? descriptor.params.size() : md.params.length;
|
||||
for (int i = start; i < params; i++) {
|
||||
if (hasDescriptor || (signFields == null || signFields.get(i) == null)) {
|
||||
if (!firstParameter) {
|
||||
int start = isEnum && init ? 2 : 0;
|
||||
for (int i = start; i < md.params.length; i++) {
|
||||
if (mask == null || mask.get(i) == null) {
|
||||
if (paramCount > 0) {
|
||||
buffer.append(", ");
|
||||
}
|
||||
|
||||
appendParameterAnnotations(buffer, mt, paramCount);
|
||||
|
||||
if (methodWrapper.varproc.getVarFinal(new VarVersionPair(index, 0)) == VarTypeProcessor.VAR_EXPLICIT_FINAL) {
|
||||
if (methodParameters != null && i < methodParameters.size()) {
|
||||
appendModifiers(buffer, methodParameters.get(i).myAccessFlags, CodeConstants.ACC_FINAL, isInterface, 0);
|
||||
}
|
||||
else if (methodWrapper.varproc.getVarFinal(new VarVersionPair(index, 0)) == VarTypeProcessor.VAR_EXPLICIT_FINAL) {
|
||||
buffer.append("final ");
|
||||
}
|
||||
|
||||
if (descriptor != null) {
|
||||
GenericType parameterType = descriptor.params.get(i);
|
||||
String typeName;
|
||||
boolean isVarArg = i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS);
|
||||
|
||||
boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arrayDim > 0);
|
||||
if (descriptor != null) {
|
||||
GenericType parameterType = descriptor.parameterTypes.get(paramCount);
|
||||
isVarArg &= parameterType.arrayDim > 0;
|
||||
if (isVarArg) {
|
||||
parameterType = parameterType.decreaseArrayDim();
|
||||
}
|
||||
|
||||
String typeName = GenericMain.getGenericCastTypeName(parameterType);
|
||||
if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) &&
|
||||
DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) {
|
||||
typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT);
|
||||
}
|
||||
|
||||
buffer.append(typeName);
|
||||
|
||||
if (isVarArg) {
|
||||
buffer.append("...");
|
||||
}
|
||||
typeName = GenericMain.getGenericCastTypeName(parameterType);
|
||||
}
|
||||
else {
|
||||
VarType parameterType = md.params[i];
|
||||
|
||||
boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arrayDim > 0);
|
||||
isVarArg &= parameterType.arrayDim > 0;
|
||||
if (isVarArg) {
|
||||
parameterType = parameterType.decreaseArrayDim();
|
||||
}
|
||||
typeName = ExprProcessor.getCastTypeName(parameterType);
|
||||
}
|
||||
|
||||
String typeName = ExprProcessor.getCastTypeName(parameterType);
|
||||
if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) &&
|
||||
DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) {
|
||||
typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT);
|
||||
}
|
||||
|
||||
buffer.append(typeName);
|
||||
|
||||
if (isVarArg) {
|
||||
buffer.append("...");
|
||||
}
|
||||
if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) &&
|
||||
DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) {
|
||||
typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT);
|
||||
}
|
||||
buffer.append(typeName);
|
||||
if (isVarArg) {
|
||||
buffer.append("...");
|
||||
}
|
||||
|
||||
buffer.append(' ');
|
||||
String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0));
|
||||
|
||||
String parameterName;
|
||||
if (methodParameters != null && i < methodParameters.size()) {
|
||||
parameterName = methodParameters.get(i).myName;
|
||||
}
|
||||
else {
|
||||
parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0));
|
||||
}
|
||||
buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors
|
||||
|
||||
firstParameter = false;
|
||||
paramCount++;
|
||||
}
|
||||
|
||||
@@ -796,8 +765,8 @@ public class ClassWriter {
|
||||
|
||||
buffer.append(')');
|
||||
|
||||
StructExceptionsAttribute attr = (StructExceptionsAttribute)mt.getAttributes().getWithKey("Exceptions");
|
||||
if ((descriptor != null && !descriptor.exceptions.isEmpty()) || attr != null) {
|
||||
StructExceptionsAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_EXCEPTIONS);
|
||||
if ((descriptor != null && !descriptor.exceptionTypes.isEmpty()) || attr != null) {
|
||||
throwsExceptions = true;
|
||||
buffer.append(" throws ");
|
||||
|
||||
@@ -805,8 +774,8 @@ public class ClassWriter {
|
||||
if (i > 0) {
|
||||
buffer.append(", ");
|
||||
}
|
||||
if (descriptor != null && !descriptor.exceptions.isEmpty()) {
|
||||
GenericType type = descriptor.exceptions.get(i);
|
||||
if (descriptor != null && !descriptor.exceptionTypes.isEmpty()) {
|
||||
GenericType type = descriptor.exceptionTypes.get(i);
|
||||
buffer.append(GenericMain.getGenericCastTypeName(type));
|
||||
}
|
||||
else {
|
||||
@@ -821,16 +790,15 @@ public class ClassWriter {
|
||||
|
||||
if ((flags & (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_NATIVE)) != 0) { // native or abstract method (explicit or interface)
|
||||
if (isAnnotation) {
|
||||
StructAnnDefaultAttribute attr = (StructAnnDefaultAttribute)mt.getAttributes().getWithKey("AnnotationDefault");
|
||||
StructAnnDefaultAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_ANNOTATION_DEFAULT);
|
||||
if (attr != null) {
|
||||
buffer.append(" default ");
|
||||
buffer.append(attr.getDefaultValue().toJava(indent + 1, new BytecodeMappingTracer())); // dummy tracer
|
||||
buffer.append(attr.getDefaultValue().toJava(0, BytecodeMappingTracer.DUMMY));
|
||||
}
|
||||
}
|
||||
|
||||
buffer.append(';');
|
||||
buffer.appendLineSeparator();
|
||||
tracer.incrementCurrentSourceLine();
|
||||
}
|
||||
else {
|
||||
if (!clinit && !dinit) {
|
||||
@@ -838,11 +806,6 @@ public class ClassWriter {
|
||||
}
|
||||
|
||||
// We do not have line information for method start, lets have it here for now
|
||||
StructLineNumberTableAttribute lineNumberTable =
|
||||
(StructLineNumberTableAttribute)mt.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE);
|
||||
if (lineNumberTable != null && DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_LINE_NUMBERS)) {
|
||||
buffer.setCurrentLine(lineNumberTable.getFirstLine() - 1);
|
||||
}
|
||||
buffer.append('{').appendLineSeparator();
|
||||
tracer.incrementCurrentSourceLine();
|
||||
|
||||
@@ -850,20 +813,20 @@ public class ClassWriter {
|
||||
|
||||
if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence
|
||||
try {
|
||||
int startLine = tracer.getCurrentSourceLine();
|
||||
// to restore in case of an exception
|
||||
BytecodeMappingTracer codeTracer = new BytecodeMappingTracer(tracer.getCurrentSourceLine());
|
||||
TextBuffer code = root.toJava(indent + 1, codeTracer);
|
||||
|
||||
TextBuffer code = root.toJava(indent + 1, tracer);
|
||||
|
||||
hideMethod = (clinit || dinit || hideConstructor(wrapper, init, throwsExceptions, paramCount)) && code.length() == 0;
|
||||
|
||||
if (!hideMethod && lineNumberTable != null && DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_LINE_NUMBERS)) {
|
||||
mapLines(code, lineNumberTable, tracer, startLine);
|
||||
}
|
||||
hideMethod = (code.length() == 0) && (clinit || dinit || hideConstructor(node, init, throwsExceptions, paramCount, flags));
|
||||
|
||||
buffer.append(code);
|
||||
|
||||
tracer.setCurrentSourceLine(codeTracer.getCurrentSourceLine());
|
||||
tracer.addTracer(codeTracer);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex);
|
||||
catch (Throwable t) {
|
||||
String message = "Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.";
|
||||
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t);
|
||||
methodWrapper.decompiledWithErrors = true;
|
||||
}
|
||||
}
|
||||
@@ -874,13 +837,13 @@ public class ClassWriter {
|
||||
buffer.appendLineSeparator();
|
||||
tracer.incrementCurrentSourceLine();
|
||||
}
|
||||
|
||||
if (root != null) {
|
||||
else if (root != null) {
|
||||
tracer.addMapping(root.getDummyExit().bytecode);
|
||||
}
|
||||
buffer.appendIndent(indent).append('}').appendLineSeparator();
|
||||
tracer.incrementCurrentSourceLine();
|
||||
}
|
||||
|
||||
tracer.incrementCurrentSourceLine();
|
||||
}
|
||||
finally {
|
||||
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper);
|
||||
@@ -893,45 +856,25 @@ public class ClassWriter {
|
||||
return !hideMethod;
|
||||
}
|
||||
|
||||
private static void mapLines(TextBuffer code, StructLineNumberTableAttribute table, BytecodeMappingTracer tracer, int startLine) {
|
||||
// build line start offsets map
|
||||
HashMap<Integer, Set<Integer>> lineStartOffsets = new HashMap<Integer, Set<Integer>>();
|
||||
for (Map.Entry<Integer, Integer> entry : tracer.getMapping().entrySet()) {
|
||||
Integer lineNumber = entry.getValue() - startLine;
|
||||
Set<Integer> curr = lineStartOffsets.get(lineNumber);
|
||||
if (curr == null) {
|
||||
curr = new TreeSet<Integer>(); // requires natural sorting!
|
||||
}
|
||||
curr.add(entry.getKey());
|
||||
lineStartOffsets.put(lineNumber, curr);
|
||||
}
|
||||
String lineSeparator = DecompilerContext.getNewLineSeparator();
|
||||
StringBuilder text = code.getOriginalText();
|
||||
int pos = text.indexOf(lineSeparator);
|
||||
int lineNumber = 0;
|
||||
while (pos != -1) {
|
||||
Set<Integer> startOffsets = lineStartOffsets.get(lineNumber);
|
||||
if (startOffsets != null) {
|
||||
for (Integer offset : startOffsets) {
|
||||
int number = table.findLineNumber(offset);
|
||||
if (number >= 0) {
|
||||
code.setLineMapping(number, pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pos = text.indexOf(lineSeparator, pos+1);
|
||||
lineNumber++;
|
||||
}
|
||||
}
|
||||
private static boolean hideConstructor(ClassNode node, boolean init, boolean throwsExceptions, int paramCount, int methodAccessFlags) {
|
||||
|
||||
private static boolean hideConstructor(ClassWrapper wrapper, boolean init, boolean throwsExceptions, int paramCount) {
|
||||
if (!init || throwsExceptions || paramCount > 0 || !DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ClassWrapper wrapper = node.getWrapper();
|
||||
StructClass cl = wrapper.getClassStruct();
|
||||
|
||||
int classAccesFlags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access;
|
||||
boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
|
||||
|
||||
// default constructor requires same accessibility flags. Exception: enum constructor which is always private
|
||||
if(!isEnum && ((classAccesFlags & ACCESSIBILITY_FLAGS) != (methodAccessFlags & ACCESSIBILITY_FLAGS))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (StructMethod mt : wrapper.getClassStruct().getMethods()) {
|
||||
for (StructMethod mt : cl.getMethods()) {
|
||||
if (CodeConstants.INIT_NAME.equals(mt.getName())) {
|
||||
if (++count > 1) {
|
||||
return false;
|
||||
@@ -1000,56 +943,87 @@ public class ClassWriter {
|
||||
buffer.appendIndent(indent).append("// $FF: ").append(comment).appendLineSeparator();
|
||||
}
|
||||
|
||||
private static final String[] ANNOTATION_ATTRIBUTES = {
|
||||
private static final StructGeneralAttribute.Key[] ANNOTATION_ATTRIBUTES = {
|
||||
StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS};
|
||||
private static final StructGeneralAttribute.Key[] PARAMETER_ANNOTATION_ATTRIBUTES = {
|
||||
StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS};
|
||||
private static final StructGeneralAttribute.Key[] TYPE_ANNOTATION_ATTRIBUTES = {
|
||||
StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS};
|
||||
|
||||
private static void appendAnnotations(TextBuffer buffer, StructMember mb, int indent) {
|
||||
private static void appendAnnotations(TextBuffer buffer, int indent, StructMember mb, int targetType) {
|
||||
Set<String> filter = new HashSet<>();
|
||||
|
||||
BytecodeMappingTracer tracer_dummy = new BytecodeMappingTracer(); // FIXME: replace with a real one
|
||||
|
||||
for (String name : ANNOTATION_ATTRIBUTES) {
|
||||
StructAnnotationAttribute attribute = (StructAnnotationAttribute)mb.getAttributes().getWithKey(name);
|
||||
for (StructGeneralAttribute.Key<?> key : ANNOTATION_ATTRIBUTES) {
|
||||
StructAnnotationAttribute attribute = (StructAnnotationAttribute)mb.getAttribute(key);
|
||||
if (attribute != null) {
|
||||
for (AnnotationExprent annotation : attribute.getAnnotations()) {
|
||||
buffer.append(annotation.toJava(indent, tracer_dummy)).appendLineSeparator();
|
||||
String text = annotation.toJava(indent, BytecodeMappingTracer.DUMMY).toString();
|
||||
filter.add(text);
|
||||
buffer.append(text).appendLineSeparator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appendTypeAnnotations(buffer, indent, mb, targetType, -1, filter);
|
||||
}
|
||||
|
||||
private static final String[] PARAMETER_ANNOTATION_ATTRIBUTES = {
|
||||
StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS};
|
||||
|
||||
private static void appendParameterAnnotations(TextBuffer buffer, StructMethod mt, int param) {
|
||||
Set<String> filter = new HashSet<>();
|
||||
|
||||
BytecodeMappingTracer tracer_dummy = new BytecodeMappingTracer(); // FIXME: replace with a real one
|
||||
|
||||
for (String name : PARAMETER_ANNOTATION_ATTRIBUTES) {
|
||||
StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute)mt.getAttributes().getWithKey(name);
|
||||
for (StructGeneralAttribute.Key<?> key : PARAMETER_ANNOTATION_ATTRIBUTES) {
|
||||
StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute)mt.getAttribute(key);
|
||||
if (attribute != null) {
|
||||
List<List<AnnotationExprent>> annotations = attribute.getParamAnnotations();
|
||||
if (param < annotations.size()) {
|
||||
for (AnnotationExprent annotation : annotations.get(param)) {
|
||||
buffer.append(annotation.toJava(0, tracer_dummy)).append(' ');
|
||||
String text = annotation.toJava(-1, BytecodeMappingTracer.DUMMY).toString();
|
||||
filter.add(text);
|
||||
buffer.append(text).append(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appendTypeAnnotations(buffer, -1, mt, TypeAnnotation.METHOD_PARAMETER, param, filter);
|
||||
}
|
||||
|
||||
private static void appendTypeAnnotations(TextBuffer buffer, int indent, StructMember mb, int targetType, int index, Set<String> filter) {
|
||||
for (StructGeneralAttribute.Key<?> key : TYPE_ANNOTATION_ATTRIBUTES) {
|
||||
StructTypeAnnotationAttribute attribute = (StructTypeAnnotationAttribute)mb.getAttribute(key);
|
||||
if (attribute != null) {
|
||||
for (TypeAnnotation annotation : attribute.getAnnotations()) {
|
||||
if (annotation.isTopLevel() && annotation.getTargetType() == targetType && (index < 0 || annotation.getIndex() == index)) {
|
||||
String text = annotation.getAnnotation().toJava(indent, BytecodeMappingTracer.DUMMY).toString();
|
||||
if (!filter.contains(text)) {
|
||||
buffer.append(text);
|
||||
if (indent < 0) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
else {
|
||||
buffer.appendLineSeparator();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<Integer, String> MODIFIERS = new LinkedHashMap<Integer, String>() {{
|
||||
put(CodeConstants.ACC_PUBLIC, "public");
|
||||
put(CodeConstants.ACC_PROTECTED, "protected");
|
||||
put(CodeConstants.ACC_PRIVATE, "private");
|
||||
put(CodeConstants.ACC_ABSTRACT, "abstract");
|
||||
put(CodeConstants.ACC_STATIC, "static");
|
||||
put(CodeConstants.ACC_FINAL, "final");
|
||||
put(CodeConstants.ACC_STRICT, "strictfp");
|
||||
put(CodeConstants.ACC_TRANSIENT, "transient");
|
||||
put(CodeConstants.ACC_VOLATILE, "volatile");
|
||||
put(CodeConstants.ACC_SYNCHRONIZED, "synchronized");
|
||||
put(CodeConstants.ACC_NATIVE, "native");
|
||||
}};
|
||||
private static final Map<Integer, String> MODIFIERS;
|
||||
static {
|
||||
MODIFIERS = new LinkedHashMap<>();
|
||||
MODIFIERS.put(CodeConstants.ACC_PUBLIC, "public");
|
||||
MODIFIERS.put(CodeConstants.ACC_PROTECTED, "protected");
|
||||
MODIFIERS.put(CodeConstants.ACC_PRIVATE, "private");
|
||||
MODIFIERS.put(CodeConstants.ACC_ABSTRACT, "abstract");
|
||||
MODIFIERS.put(CodeConstants.ACC_STATIC, "static");
|
||||
MODIFIERS.put(CodeConstants.ACC_FINAL, "final");
|
||||
MODIFIERS.put(CodeConstants.ACC_STRICT, "strictfp");
|
||||
MODIFIERS.put(CodeConstants.ACC_TRANSIENT, "transient");
|
||||
MODIFIERS.put(CodeConstants.ACC_VOLATILE, "volatile");
|
||||
MODIFIERS.put(CodeConstants.ACC_SYNCHRONIZED, "synchronized");
|
||||
MODIFIERS.put(CodeConstants.ACC_NATIVE, "native");
|
||||
}
|
||||
|
||||
private static final int CLASS_ALLOWED =
|
||||
CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE | CodeConstants.ACC_ABSTRACT |
|
||||
@@ -1059,12 +1033,15 @@ public class ClassWriter {
|
||||
CodeConstants.ACC_FINAL | CodeConstants.ACC_TRANSIENT | CodeConstants.ACC_VOLATILE;
|
||||
private static final int METHOD_ALLOWED =
|
||||
CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE | CodeConstants.ACC_ABSTRACT |
|
||||
CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL | CodeConstants.ACC_SYNCHRONIZED | CodeConstants.ACC_NATIVE | CodeConstants.ACC_STRICT;
|
||||
CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL | CodeConstants.ACC_SYNCHRONIZED | CodeConstants.ACC_NATIVE |
|
||||
CodeConstants.ACC_STRICT;
|
||||
|
||||
private static final int CLASS_EXCLUDED = CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_STATIC;
|
||||
private static final int FIELD_EXCLUDED = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL;
|
||||
private static final int METHOD_EXCLUDED = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_ABSTRACT;
|
||||
|
||||
private static final int ACCESSIBILITY_FLAGS = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE;
|
||||
|
||||
private static void appendModifiers(TextBuffer buffer, int flags, int allowed, boolean isInterface, int excluded) {
|
||||
flags &= allowed;
|
||||
if (!isInterface) excluded = 0;
|
||||
@@ -1075,7 +1052,17 @@ public class ClassWriter {
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendTypeParameters(TextBuffer buffer, List<String> parameters, List<List<GenericType>> bounds) {
|
||||
public static GenericClassDescriptor getGenericClassDescriptor(StructClass cl) {
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
|
||||
StructGenericSignatureAttribute attr = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE);
|
||||
if (attr != null) {
|
||||
return GenericMain.parseClassSignature(attr.getSignature());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void appendTypeParameters(TextBuffer buffer, List<String> parameters, List<? extends List<GenericType>> bounds) {
|
||||
buffer.append('<');
|
||||
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
@@ -1098,4 +1085,4 @@ public class ClassWriter {
|
||||
|
||||
buffer.append('>');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,10 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
import org.jetbrains.java.decompiler.code.InstructionSequence;
|
||||
import org.jetbrains.java.decompiler.main.collectors.BytecodeSourceMapper;
|
||||
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
|
||||
import org.jetbrains.java.decompiler.main.collectors.ImportCollector;
|
||||
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
|
||||
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
|
||||
@@ -31,105 +18,99 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
|
||||
import org.jetbrains.java.decompiler.struct.StructClass;
|
||||
import org.jetbrains.java.decompiler.struct.StructContext;
|
||||
import org.jetbrains.java.decompiler.struct.StructMethod;
|
||||
import org.jetbrains.java.decompiler.struct.attr.StructEnclosingMethodAttribute;
|
||||
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
|
||||
import org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute;
|
||||
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
import org.jetbrains.java.decompiler.util.TextBuffer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class ClassesProcessor {
|
||||
|
||||
public class ClassesProcessor implements CodeConstants {
|
||||
public static final int AVERAGE_CLASS_SIZE = 16 * 1024;
|
||||
|
||||
private final Map<String, ClassNode> mapRootClasses = new HashMap<String, ClassNode>();
|
||||
private final StructContext context;
|
||||
private final Map<String, ClassNode> mapRootClasses = new HashMap<>();
|
||||
|
||||
private static class Inner {
|
||||
private String simpleName;
|
||||
private int type;
|
||||
private int accessFlags;
|
||||
|
||||
private static boolean equal(Inner o1, Inner o2) {
|
||||
return o1.type == o2.type && o1.accessFlags == o2.accessFlags && InterpreterUtil.equalObjects(o1.simpleName, o2.simpleName);
|
||||
}
|
||||
}
|
||||
|
||||
public ClassesProcessor(StructContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
HashMap<String, Object[]> mapInnerClasses = new HashMap<String, Object[]>();
|
||||
HashMap<String, HashSet<String>> mapNestedClassReferences = new HashMap<String, HashSet<String>>();
|
||||
HashMap<String, HashSet<String>> mapEnclosingClassReferences = new HashMap<String, HashSet<String>>();
|
||||
HashMap<String, String> mapNewSimpleNames = new HashMap<String, String>();
|
||||
public void loadClasses(IIdentifierRenamer renamer) {
|
||||
Map<String, Inner> mapInnerClasses = new HashMap<>();
|
||||
Map<String, Set<String>> mapNestedClassReferences = new HashMap<>();
|
||||
Map<String, Set<String>> mapEnclosingClassReferences = new HashMap<>();
|
||||
Map<String, String> mapNewSimpleNames = new HashMap<>();
|
||||
|
||||
boolean bDecompileInner = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_INNER);
|
||||
boolean verifyAnonymousClasses = DecompilerContext.getOption(IFernflowerPreferences.VERIFY_ANONYMOUS_CLASSES);
|
||||
|
||||
// create class nodes
|
||||
for (StructClass cl : context.getClasses().values()) {
|
||||
if (cl.isOwn() && !mapRootClasses.containsKey(cl.qualifiedName)) {
|
||||
|
||||
if (bDecompileInner) {
|
||||
StructInnerClassesAttribute inner = (StructInnerClassesAttribute)cl.getAttributes().getWithKey("InnerClasses");
|
||||
StructInnerClassesAttribute inner = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES);
|
||||
|
||||
if (inner != null) {
|
||||
|
||||
for (int i = 0; i < inner.getClassEntries().size(); i++) {
|
||||
|
||||
int[] entry = inner.getClassEntries().get(i);
|
||||
String[] strentry = inner.getStringEntries().get(i);
|
||||
|
||||
Object[] arr = new Object[4]; // arr[0] not used
|
||||
|
||||
String innername = strentry[0];
|
||||
|
||||
// nested class type
|
||||
arr[2] = entry[1] == 0 ? (entry[2] == 0 ? ClassNode.CLASS_ANONYMOUS : ClassNode.CLASS_LOCAL) : ClassNode.CLASS_MEMBER;
|
||||
for (StructInnerClassesAttribute.Entry entry : inner.getEntries()) {
|
||||
String innerName = entry.innerName;
|
||||
|
||||
// original simple name
|
||||
String simpleName = strentry[2];
|
||||
String savedName = mapNewSimpleNames.get(innername);
|
||||
|
||||
String simpleName = entry.simpleName;
|
||||
String savedName = mapNewSimpleNames.get(innerName);
|
||||
if (savedName != null) {
|
||||
simpleName = savedName;
|
||||
}
|
||||
else if (simpleName != null && DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) {
|
||||
IIdentifierRenamer renamer = DecompilerContext.getPoolInterceptor().getHelper();
|
||||
if (renamer.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_CLASS, simpleName, null, null)) {
|
||||
simpleName = renamer.getNextClassName(innername, simpleName);
|
||||
mapNewSimpleNames.put(innername, simpleName);
|
||||
}
|
||||
else if (simpleName != null &&
|
||||
renamer != null &&
|
||||
renamer.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_CLASS, simpleName, null, null)) {
|
||||
simpleName = renamer.getNextClassName(innerName, simpleName);
|
||||
mapNewSimpleNames.put(innerName, simpleName);
|
||||
}
|
||||
|
||||
arr[1] = simpleName;
|
||||
|
||||
// original access flags
|
||||
arr[3] = entry[3];
|
||||
Inner rec = new Inner();
|
||||
rec.simpleName = simpleName;
|
||||
rec.type = entry.simpleNameIdx == 0 ? ClassNode.CLASS_ANONYMOUS : entry.outerNameIdx == 0 ? ClassNode.CLASS_LOCAL : ClassNode.CLASS_MEMBER;
|
||||
rec.accessFlags = entry.accessFlags;
|
||||
|
||||
// enclosing class
|
||||
String enclClassName;
|
||||
if (entry[1] != 0) {
|
||||
enclClassName = strentry[1];
|
||||
String enclClassName = entry.outerNameIdx != 0 ? entry.enclosingName : cl.qualifiedName;
|
||||
if (enclClassName == null || innerName.equals(enclClassName)) {
|
||||
continue; // invalid name or self reference
|
||||
}
|
||||
else {
|
||||
enclClassName = cl.qualifiedName;
|
||||
if (rec.type == ClassNode.CLASS_MEMBER && !innerName.equals(enclClassName + '$' + entry.simpleName)) {
|
||||
continue; // not a real inner class
|
||||
}
|
||||
|
||||
if (!innername.equals(enclClassName)) { // self reference
|
||||
StructClass enclosing_class = context.getClasses().get(enclClassName);
|
||||
if (enclosing_class != null && enclosing_class.isOwn()) { // own classes only
|
||||
|
||||
Object[] arrold = mapInnerClasses.get(innername);
|
||||
if (arrold == null) {
|
||||
mapInnerClasses.put(innername, arr);
|
||||
}
|
||||
else if (!InterpreterUtil.equalObjectArrays(arrold, arr)) {
|
||||
String message = "Inconsistent inner class entries for " + innername + "!";
|
||||
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
|
||||
}
|
||||
|
||||
// reference to the nested class
|
||||
HashSet<String> set = mapNestedClassReferences.get(enclClassName);
|
||||
if (set == null) {
|
||||
mapNestedClassReferences.put(enclClassName, set = new HashSet<String>());
|
||||
}
|
||||
set.add(innername);
|
||||
|
||||
// reference to the enclosing class
|
||||
set = mapEnclosingClassReferences.get(innername);
|
||||
if (set == null) {
|
||||
mapEnclosingClassReferences.put(innername, set = new HashSet<String>());
|
||||
}
|
||||
set.add(enclClassName);
|
||||
StructClass enclosingClass = context.getClasses().get(enclClassName);
|
||||
if (enclosingClass != null && enclosingClass.isOwn()) { // own classes only
|
||||
Inner existingRec = mapInnerClasses.get(innerName);
|
||||
if (existingRec == null) {
|
||||
mapInnerClasses.put(innerName, rec);
|
||||
}
|
||||
else if (!Inner.equal(existingRec, rec)) {
|
||||
String message = "Inconsistent inner class entries for " + innerName + "!";
|
||||
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
|
||||
}
|
||||
|
||||
// reference to the nested class
|
||||
mapNestedClassReferences.computeIfAbsent(enclClassName, k -> new HashSet<>()).add(innerName);
|
||||
// reference to the enclosing class
|
||||
mapEnclosingClassReferences.computeIfAbsent(innerName, k -> new HashSet<>()).add(enclClassName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,30 +123,32 @@ public class ClassesProcessor {
|
||||
}
|
||||
|
||||
if (bDecompileInner) {
|
||||
|
||||
// connect nested classes
|
||||
for (Entry<String, ClassNode> ent : mapRootClasses.entrySet()) {
|
||||
// root class?
|
||||
if (!mapInnerClasses.containsKey(ent.getKey())) {
|
||||
|
||||
HashSet<String> setVisited = new HashSet<String>();
|
||||
LinkedList<String> stack = new LinkedList<String>();
|
||||
Set<String> setVisited = new HashSet<>();
|
||||
LinkedList<String> stack = new LinkedList<>();
|
||||
|
||||
stack.add(ent.getKey());
|
||||
setVisited.add(ent.getKey());
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
|
||||
String superClass = stack.removeFirst();
|
||||
ClassNode supernode = mapRootClasses.get(superClass);
|
||||
ClassNode superNode = mapRootClasses.get(superClass);
|
||||
|
||||
HashSet<String> setNestedClasses = mapNestedClassReferences.get(superClass);
|
||||
Set<String> setNestedClasses = mapNestedClassReferences.get(superClass);
|
||||
if (setNestedClasses != null) {
|
||||
StructClass scl = superNode.classStruct;
|
||||
StructInnerClassesAttribute inner = scl.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES);
|
||||
|
||||
StructClass scl = supernode.classStruct;
|
||||
StructInnerClassesAttribute inner = (StructInnerClassesAttribute)scl.getAttributes().getWithKey("InnerClasses");
|
||||
for (int i = 0; i < inner.getStringEntries().size(); i++) {
|
||||
String nestedClass = inner.getStringEntries().get(i)[0];
|
||||
if (inner == null || inner.getEntries().isEmpty()) {
|
||||
DecompilerContext.getLogger().writeMessage(superClass + " does not contain inner classes!", IFernflowerLogger.Severity.WARN);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (StructInnerClassesAttribute.Entry entry : inner.getEntries()) {
|
||||
String nestedClass = entry.innerName;
|
||||
if (!setNestedClasses.contains(nestedClass)) {
|
||||
continue;
|
||||
}
|
||||
@@ -174,52 +157,49 @@ public class ClassesProcessor {
|
||||
continue;
|
||||
}
|
||||
|
||||
ClassNode nestednode = mapRootClasses.get(nestedClass);
|
||||
if (nestednode == null) {
|
||||
ClassNode nestedNode = mapRootClasses.get(nestedClass);
|
||||
if (nestedNode == null) {
|
||||
DecompilerContext.getLogger().writeMessage("Nested class " + nestedClass + " missing!", IFernflowerLogger.Severity.WARN);
|
||||
continue;
|
||||
}
|
||||
|
||||
Object[] arr = mapInnerClasses.get(nestedClass);
|
||||
Inner rec = mapInnerClasses.get(nestedClass);
|
||||
|
||||
//if ((Integer)arr[2] == ClassNode.CLASS_MEMBER) {
|
||||
// FIXME: check for consistent naming
|
||||
//}
|
||||
|
||||
nestednode.type = (Integer)arr[2];
|
||||
nestednode.simpleName = (String)arr[1];
|
||||
nestednode.access = (Integer)arr[3];
|
||||
nestedNode.simpleName = rec.simpleName;
|
||||
nestedNode.type = rec.type;
|
||||
nestedNode.access = rec.accessFlags;
|
||||
|
||||
if (nestednode.type == ClassNode.CLASS_ANONYMOUS) {
|
||||
StructClass cl = nestednode.classStruct;
|
||||
// sanity checks of the class supposed to be anonymous
|
||||
if (verifyAnonymousClasses && nestedNode.type == ClassNode.CLASS_ANONYMOUS && !isAnonymous(nestedNode.classStruct, scl)) {
|
||||
nestedNode.type = ClassNode.CLASS_LOCAL;
|
||||
}
|
||||
|
||||
// remove static if anonymous class
|
||||
// a common compiler bug
|
||||
nestednode.access &= ~CodeConstants.ACC_STATIC;
|
||||
if (nestedNode.type == ClassNode.CLASS_ANONYMOUS) {
|
||||
StructClass cl = nestedNode.classStruct;
|
||||
// remove static if anonymous class (a common compiler bug)
|
||||
nestedNode.access &= ~CodeConstants.ACC_STATIC;
|
||||
|
||||
int[] interfaces = cl.getInterfaces();
|
||||
|
||||
if (interfaces.length > 0) {
|
||||
if (interfaces.length > 1) {
|
||||
String message = "Inconsistent anonymous class definition: " + cl.qualifiedName;
|
||||
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
|
||||
}
|
||||
nestednode.anonymousClassType = new VarType(cl.getInterface(0), true);
|
||||
nestedNode.anonymousClassType = new VarType(cl.getInterface(0), true);
|
||||
}
|
||||
else {
|
||||
nestednode.anonymousClassType = new VarType(cl.superClass.getString(), true);
|
||||
nestedNode.anonymousClassType = new VarType(cl.superClass.getString(), true);
|
||||
}
|
||||
}
|
||||
else if (nestednode.type == ClassNode.CLASS_LOCAL) {
|
||||
// only abstract and final are permitted
|
||||
// a common compiler bug
|
||||
nestednode.access &= (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_FINAL);
|
||||
else if (nestedNode.type == ClassNode.CLASS_LOCAL) {
|
||||
// only abstract and final are permitted (a common compiler bug)
|
||||
nestedNode.access &= (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_FINAL);
|
||||
}
|
||||
|
||||
supernode.nested.add(nestednode);
|
||||
nestednode.parent = supernode;
|
||||
superNode.nested.add(nestedNode);
|
||||
nestedNode.parent = superNode;
|
||||
|
||||
nestednode.enclosingClasses.addAll(mapEnclosingClassReferences.get(nestedClass));
|
||||
nestedNode.enclosingClasses.addAll(mapEnclosingClassReferences.get(nestedClass));
|
||||
|
||||
stack.add(nestedClass);
|
||||
}
|
||||
@@ -230,6 +210,92 @@ public class ClassesProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAnonymous(StructClass cl, StructClass enclosingCl) {
|
||||
// checking super class and interfaces
|
||||
int[] interfaces = cl.getInterfaces();
|
||||
if (interfaces.length > 0) {
|
||||
boolean hasNonTrivialSuperClass = cl.superClass != null && !VarType.VARTYPE_OBJECT.equals(new VarType(cl.superClass.getString(), true));
|
||||
if (hasNonTrivialSuperClass || interfaces.length > 1) { // can't have multiple 'sources'
|
||||
String message = "Inconsistent anonymous class definition: '" + cl.qualifiedName + "'. Multiple interfaces and/or super class defined.";
|
||||
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (cl.superClass == null) { // neither interface nor super class defined
|
||||
String message = "Inconsistent anonymous class definition: '" + cl.qualifiedName + "'. Neither interface nor super class defined.";
|
||||
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: check constructors
|
||||
// FIXME: check enclosing class/method
|
||||
|
||||
ConstantPool pool = enclosingCl.getPool();
|
||||
|
||||
int refCounter = 0;
|
||||
boolean refNotNew = false;
|
||||
|
||||
StructEnclosingMethodAttribute attribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_ENCLOSING_METHOD);
|
||||
String enclosingMethod = attribute != null ? attribute.getMethodName() : null;
|
||||
|
||||
// checking references in the enclosing class
|
||||
for (StructMethod mt : enclosingCl.getMethods()) {
|
||||
if (enclosingMethod != null && !enclosingMethod.equals(mt.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
mt.expandData();
|
||||
|
||||
InstructionSequence seq = mt.getInstructionSequence();
|
||||
if (seq != null) {
|
||||
int len = seq.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
Instruction instr = seq.getInstr(i);
|
||||
switch (instr.opcode) {
|
||||
case opc_checkcast:
|
||||
case opc_instanceof:
|
||||
if (cl.qualifiedName.equals(pool.getPrimitiveConstant(instr.operand(0)).getString())) {
|
||||
refCounter++;
|
||||
refNotNew = true;
|
||||
}
|
||||
break;
|
||||
case opc_new:
|
||||
case opc_anewarray:
|
||||
case opc_multianewarray:
|
||||
if (cl.qualifiedName.equals(pool.getPrimitiveConstant(instr.operand(0)).getString())) {
|
||||
refCounter++;
|
||||
}
|
||||
break;
|
||||
case opc_getstatic:
|
||||
case opc_putstatic:
|
||||
if (cl.qualifiedName.equals(pool.getLinkConstant(instr.operand(0)).classname)) {
|
||||
refCounter++;
|
||||
refNotNew = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mt.releaseResources();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
String message = "Could not read method while checking anonymous class definition: '" + enclosingCl.qualifiedName + "', '" +
|
||||
InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()) + "'";
|
||||
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (refCounter > 1 || refNotNew) {
|
||||
String message = "Inconsistent references to the class '" + cl.qualifiedName + "' which is supposed to be anonymous";
|
||||
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void writeClass(StructClass cl, TextBuffer buffer) throws IOException {
|
||||
ClassNode root = mapRootClasses.get(cl.qualifiedName);
|
||||
if (root.type != ClassNode.CLASS_ROOT) {
|
||||
@@ -239,9 +305,7 @@ public class ClassesProcessor {
|
||||
DecompilerContext.getLogger().startReadingClass(cl.qualifiedName);
|
||||
try {
|
||||
ImportCollector importCollector = new ImportCollector(root);
|
||||
DecompilerContext.setImportCollector(importCollector);
|
||||
DecompilerContext.setCounterContainer(new CounterContainer());
|
||||
DecompilerContext.setBytecodeSourceMapper(new BytecodeSourceMapper());
|
||||
DecompilerContext.startClass(importCollector);
|
||||
|
||||
new LambdaProcessor().processClass(root);
|
||||
|
||||
@@ -258,11 +322,8 @@ public class ClassesProcessor {
|
||||
TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
|
||||
new ClassWriter().classToJava(root, classBuffer, 0, null);
|
||||
|
||||
int total_offset_lines = 0;
|
||||
|
||||
int index = cl.qualifiedName.lastIndexOf("/");
|
||||
if (index >= 0) {
|
||||
total_offset_lines+=2;
|
||||
String packageName = cl.qualifiedName.substring(0, index).replace('/', '.');
|
||||
|
||||
buffer.append("package ");
|
||||
@@ -275,16 +336,15 @@ public class ClassesProcessor {
|
||||
int import_lines_written = importCollector.writeImports(buffer);
|
||||
if (import_lines_written > 0) {
|
||||
buffer.appendLineSeparator();
|
||||
total_offset_lines += import_lines_written + 1;
|
||||
}
|
||||
//buffer.append(lineSeparator);
|
||||
|
||||
total_offset_lines = buffer.countLines();
|
||||
int offsetLines = buffer.countLines();
|
||||
|
||||
buffer.append(classBuffer);
|
||||
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) {
|
||||
BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper();
|
||||
mapper.addTotalOffset(total_offset_lines);
|
||||
mapper.addTotalOffset(offsetLines);
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_ORIGINAL_LINES)) {
|
||||
buffer.dumpOriginalLineNumbers(mapper.getOriginalLinesMapping());
|
||||
}
|
||||
@@ -300,8 +360,7 @@ public class ClassesProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private static void initWrappers(ClassNode node) throws IOException {
|
||||
|
||||
private static void initWrappers(ClassNode node) {
|
||||
if (node.type == ClassNode.CLASS_LAMBDA) {
|
||||
return;
|
||||
}
|
||||
@@ -317,7 +376,6 @@ public class ClassesProcessor {
|
||||
}
|
||||
|
||||
private static void addClassnameToImport(ClassNode node, ImportCollector imp) {
|
||||
|
||||
if (node.simpleName != null && node.simpleName.length() > 0) {
|
||||
imp.getShortName(node.type == ClassNode.CLASS_ROOT ? node.classStruct.qualifiedName : node.simpleName, false);
|
||||
}
|
||||
@@ -328,7 +386,6 @@ public class ClassesProcessor {
|
||||
}
|
||||
|
||||
private static void destroyWrappers(ClassNode node) {
|
||||
|
||||
node.wrapper = null;
|
||||
node.classStruct.releaseResources();
|
||||
|
||||
@@ -343,7 +400,6 @@ public class ClassesProcessor {
|
||||
|
||||
|
||||
public static class ClassNode {
|
||||
|
||||
public static final int CLASS_ROOT = 0;
|
||||
public static final int CLASS_MEMBER = 1;
|
||||
public static final int CLASS_ANONYMOUS = 2;
|
||||
@@ -357,13 +413,12 @@ public class ClassesProcessor {
|
||||
private ClassWrapper wrapper;
|
||||
public String enclosingMethod;
|
||||
public InvocationExprent superInvocation;
|
||||
public final Map<String, VarVersionPair> mapFieldsToVars = new HashMap<String, VarVersionPair>();
|
||||
public final Map<String, VarVersionPair> mapFieldsToVars = new HashMap<>();
|
||||
public VarType anonymousClassType;
|
||||
public final List<ClassNode> nested = new ArrayList<ClassNode>();
|
||||
public final Set<String> enclosingClasses = new HashSet<String>();
|
||||
public final List<ClassNode> nested = new ArrayList<>();
|
||||
public final Set<String> enclosingClasses = new HashSet<>();
|
||||
public ClassNode parent;
|
||||
public LambdaInformation lambdaInformation;
|
||||
public boolean namelessConstructorStub = false;
|
||||
|
||||
public ClassNode(String content_class_name,
|
||||
String content_method_name,
|
||||
@@ -378,7 +433,6 @@ public class ClassesProcessor {
|
||||
|
||||
lambdaInformation = new LambdaInformation();
|
||||
|
||||
lambdaInformation.class_name = lambda_class_name;
|
||||
lambdaInformation.method_name = lambda_method_name;
|
||||
lambdaInformation.method_descriptor = lambda_method_descriptor;
|
||||
|
||||
@@ -428,7 +482,6 @@ public class ClassesProcessor {
|
||||
}
|
||||
|
||||
public static class LambdaInformation {
|
||||
public String class_name;
|
||||
public String method_name;
|
||||
public String method_descriptor;
|
||||
|
||||
@@ -442,4 +495,4 @@ public class ClassesProcessor {
|
||||
public boolean is_content_method_static;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +1,57 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main;
|
||||
|
||||
import org.jetbrains.java.decompiler.main.collectors.BytecodeSourceMapper;
|
||||
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
|
||||
import org.jetbrains.java.decompiler.main.collectors.ImportCollector;
|
||||
import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
|
||||
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
|
||||
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
|
||||
import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor;
|
||||
import org.jetbrains.java.decompiler.struct.StructContext;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class DecompilerContext {
|
||||
public static final String CURRENT_CLASS = "CURRENT_CLASS";
|
||||
public static final String CURRENT_CLASS_WRAPPER = "CURRENT_CLASS_WRAPPER";
|
||||
public static final String CURRENT_CLASS_NODE = "CURRENT_CLASS_NODE";
|
||||
public static final String CURRENT_METHOD = "CURRENT_METHOD";
|
||||
public static final String CURRENT_METHOD_DESCRIPTOR = "CURRENT_METHOD_DESCRIPTOR";
|
||||
public static final String CURRENT_METHOD_WRAPPER = "CURRENT_METHOD_WRAPPER";
|
||||
public static final String CURRENT_VAR_PROCESSOR = "CURRENT_VAR_PROCESSOR";
|
||||
|
||||
private static final ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<DecompilerContext>();
|
||||
|
||||
private final Map<String, Object> properties;
|
||||
private StructContext structContext;
|
||||
private final IFernflowerLogger logger;
|
||||
private final StructContext structContext;
|
||||
private final ClassesProcessor classProcessor;
|
||||
private final PoolInterceptor poolInterceptor;
|
||||
private ImportCollector importCollector;
|
||||
private VarNamesCollector varNamescollector;
|
||||
private VarProcessor varProcessor;
|
||||
private CounterContainer counterContainer;
|
||||
private ClassesProcessor classProcessor;
|
||||
private PoolInterceptor poolInterceptor;
|
||||
private IFernflowerLogger logger;
|
||||
private BytecodeSourceMapper bytecodeSourceMapper;
|
||||
|
||||
private DecompilerContext(Map<String, Object> properties) {
|
||||
public DecompilerContext(Map<String, Object> properties,
|
||||
IFernflowerLogger logger,
|
||||
StructContext structContext,
|
||||
ClassesProcessor classProcessor,
|
||||
PoolInterceptor interceptor) {
|
||||
Objects.requireNonNull(properties);
|
||||
Objects.requireNonNull(logger);
|
||||
Objects.requireNonNull(structContext);
|
||||
Objects.requireNonNull(classProcessor);
|
||||
|
||||
this.properties = properties;
|
||||
this.logger = logger;
|
||||
this.structContext = structContext;
|
||||
this.classProcessor = classProcessor;
|
||||
this.poolInterceptor = interceptor;
|
||||
this.counterContainer = new CounterContainer();
|
||||
}
|
||||
|
||||
public static void initContext(Map<String, Object> propertiesCustom) {
|
||||
Map<String, Object> properties = new HashMap<String, Object>(IFernflowerPreferences.DEFAULTS);
|
||||
if (propertiesCustom != null) {
|
||||
properties.putAll(propertiesCustom);
|
||||
}
|
||||
currentContext.set(new DecompilerContext(properties));
|
||||
}
|
||||
// *****************************************************************************
|
||||
// context setup and update
|
||||
// *****************************************************************************
|
||||
|
||||
private static final ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<>();
|
||||
|
||||
public static DecompilerContext getCurrentContext() {
|
||||
return currentContext.get();
|
||||
@@ -69,93 +61,69 @@ public class DecompilerContext {
|
||||
currentContext.set(context);
|
||||
}
|
||||
|
||||
public static Object getProperty(String key) {
|
||||
return getCurrentContext().properties.get(key);
|
||||
}
|
||||
|
||||
public static void setProperty(String key, Object value) {
|
||||
getCurrentContext().properties.put(key, value);
|
||||
}
|
||||
|
||||
public static void startClass(ImportCollector importCollector) {
|
||||
DecompilerContext context = getCurrentContext();
|
||||
context.importCollector = importCollector;
|
||||
context.counterContainer = new CounterContainer();
|
||||
context.bytecodeSourceMapper = new BytecodeSourceMapper();
|
||||
}
|
||||
|
||||
public static void startMethod(VarProcessor varProcessor) {
|
||||
DecompilerContext context = getCurrentContext();
|
||||
context.varProcessor = varProcessor;
|
||||
context.counterContainer = new CounterContainer();
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// context access
|
||||
// *****************************************************************************
|
||||
|
||||
public static Object getProperty(String key) {
|
||||
return getCurrentContext().properties.get(key);
|
||||
}
|
||||
|
||||
public static boolean getOption(String key) {
|
||||
return "1".equals(getCurrentContext().properties.get(key));
|
||||
return "1".equals(getProperty(key));
|
||||
}
|
||||
|
||||
public static ImportCollector getImportCollector() {
|
||||
return getCurrentContext().importCollector;
|
||||
}
|
||||
|
||||
public static void setImportCollector(ImportCollector importCollector) {
|
||||
getCurrentContext().importCollector = importCollector;
|
||||
}
|
||||
|
||||
public static VarNamesCollector getVarNamesCollector() {
|
||||
return getCurrentContext().varNamescollector;
|
||||
}
|
||||
|
||||
public static void setVarNamesCollector(VarNamesCollector varNamesCollector) {
|
||||
getCurrentContext().varNamescollector = varNamesCollector;
|
||||
}
|
||||
|
||||
public static StructContext getStructContext() {
|
||||
return getCurrentContext().structContext;
|
||||
}
|
||||
|
||||
public static void setStructContext(StructContext structContext) {
|
||||
getCurrentContext().structContext = structContext;
|
||||
}
|
||||
|
||||
public static CounterContainer getCounterContainer() {
|
||||
return getCurrentContext().counterContainer;
|
||||
}
|
||||
|
||||
public static void setCounterContainer(CounterContainer counterContainer) {
|
||||
getCurrentContext().counterContainer = counterContainer;
|
||||
}
|
||||
|
||||
public static ClassesProcessor getClassProcessor() {
|
||||
return getCurrentContext().classProcessor;
|
||||
}
|
||||
|
||||
public static void setClassProcessor(ClassesProcessor classProcessor) {
|
||||
getCurrentContext().classProcessor = classProcessor;
|
||||
}
|
||||
|
||||
public static PoolInterceptor getPoolInterceptor() {
|
||||
return getCurrentContext().poolInterceptor;
|
||||
}
|
||||
|
||||
public static void setPoolInterceptor(PoolInterceptor poolinterceptor) {
|
||||
getCurrentContext().poolInterceptor = poolinterceptor;
|
||||
}
|
||||
|
||||
public static BytecodeSourceMapper getBytecodeSourceMapper() {
|
||||
return getCurrentContext().bytecodeSourceMapper;
|
||||
}
|
||||
|
||||
public static void setBytecodeSourceMapper(BytecodeSourceMapper bytecodeSourceMapper) {
|
||||
getCurrentContext().bytecodeSourceMapper = bytecodeSourceMapper;
|
||||
public static String getNewLineSeparator() {
|
||||
return getOption(IFernflowerPreferences.NEW_LINE_SEPARATOR) ?
|
||||
IFernflowerPreferences.LINE_SEPARATOR_UNX : IFernflowerPreferences.LINE_SEPARATOR_WIN;
|
||||
}
|
||||
|
||||
public static IFernflowerLogger getLogger() {
|
||||
return getCurrentContext().logger;
|
||||
}
|
||||
|
||||
public static void setLogger(IFernflowerLogger logger) {
|
||||
if (logger != null) {
|
||||
String level = (String)getProperty(IFernflowerPreferences.LOG_LEVEL);
|
||||
if (level != null) {
|
||||
try {
|
||||
logger.setSeverity(IFernflowerLogger.Severity.valueOf(level.toUpperCase(Locale.US)));
|
||||
}
|
||||
catch (IllegalArgumentException ignore) { }
|
||||
}
|
||||
}
|
||||
getCurrentContext().logger = logger;
|
||||
public static StructContext getStructContext() {
|
||||
return getCurrentContext().structContext;
|
||||
}
|
||||
|
||||
public static String getNewLineSeparator() {
|
||||
return getOption(IFernflowerPreferences.NEW_LINE_SEPARATOR) ?
|
||||
IFernflowerPreferences.LINE_SEPARATOR_LIN : IFernflowerPreferences.LINE_SEPARATOR_WIN;
|
||||
public static ClassesProcessor getClassProcessor() {
|
||||
return getCurrentContext().classProcessor;
|
||||
}
|
||||
}
|
||||
|
||||
public static PoolInterceptor getPoolInterceptor() {
|
||||
return getCurrentContext().poolInterceptor;
|
||||
}
|
||||
|
||||
public static ImportCollector getImportCollector() {
|
||||
return getCurrentContext().importCollector;
|
||||
}
|
||||
|
||||
public static VarProcessor getVarProcessor() {
|
||||
return getCurrentContext().varProcessor;
|
||||
}
|
||||
|
||||
public static CounterContainer getCounterContainer() {
|
||||
return getCurrentContext().counterContainer;
|
||||
}
|
||||
|
||||
public static BytecodeSourceMapper getBytecodeSourceMapper() {
|
||||
return getCurrentContext().bytecodeSourceMapper;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
@@ -20,16 +6,14 @@ import org.jetbrains.java.decompiler.main.rels.ClassWrapper;
|
||||
import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statements;
|
||||
import org.jetbrains.java.decompiler.struct.StructClass;
|
||||
import org.jetbrains.java.decompiler.struct.StructField;
|
||||
import org.jetbrains.java.decompiler.struct.StructMethod;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
|
||||
public class EnumProcessor {
|
||||
|
||||
public static void clearEnum(ClassWrapper wrapper) {
|
||||
StructClass cl = wrapper.getClassStruct();
|
||||
|
||||
@@ -50,12 +34,12 @@ public class EnumProcessor {
|
||||
}
|
||||
}
|
||||
else if (CodeConstants.INIT_NAME.equals(name)) {
|
||||
Statement firstData = findFirstData(method.root);
|
||||
Statement firstData = Statements.findFirstData(method.root);
|
||||
if (firstData != null && !firstData.getExprents().isEmpty()) {
|
||||
Exprent exprent = firstData.getExprents().get(0);
|
||||
if (exprent.type == Exprent.EXPRENT_INVOCATION) {
|
||||
InvocationExprent invexpr = (InvocationExprent)exprent;
|
||||
if (isInvocationSuperConstructor(invexpr, method, wrapper)) {
|
||||
InvocationExprent invExpr = (InvocationExprent)exprent;
|
||||
if (Statements.isInvocationInitConstructor(invExpr, method, wrapper, false)) {
|
||||
firstData.getExprents().remove(0);
|
||||
}
|
||||
}
|
||||
@@ -71,49 +55,4 @@ public class EnumProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: move to a util class (see also InitializerProcessor)
|
||||
private static Statement findFirstData(Statement stat) {
|
||||
|
||||
if (stat.getExprents() != null) {
|
||||
return stat;
|
||||
}
|
||||
else {
|
||||
if (stat.isLabeled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (stat.type) {
|
||||
case Statement.TYPE_SEQUENCE:
|
||||
case Statement.TYPE_IF:
|
||||
case Statement.TYPE_ROOT:
|
||||
case Statement.TYPE_SWITCH:
|
||||
case Statement.TYPE_SYNCRONIZED:
|
||||
return findFirstData(stat.getFirst());
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: move to util class (see also InitializerProcessor)
|
||||
private static boolean isInvocationSuperConstructor(InvocationExprent inv, MethodWrapper meth, ClassWrapper wrapper) {
|
||||
|
||||
if (inv.getFunctype() == InvocationExprent.TYP_INIT) {
|
||||
if (inv.getInstance().type == Exprent.EXPRENT_VAR) {
|
||||
VarExprent instvar = (VarExprent)inv.getInstance();
|
||||
VarVersionPair varpaar = new VarVersionPair(instvar);
|
||||
|
||||
String classname = meth.varproc.getThisVars().get(varpaar);
|
||||
|
||||
if (classname != null) { // any this instance. TODO: Restrict to current class?
|
||||
if (!wrapper.getClassStruct().qualifiedName.equals(inv.getClassname())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +1,88 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main;
|
||||
|
||||
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
|
||||
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
|
||||
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;
|
||||
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
|
||||
import org.jetbrains.java.decompiler.main.extern.IResultSaver;
|
||||
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
|
||||
import org.jetbrains.java.decompiler.main.extern.*;
|
||||
import org.jetbrains.java.decompiler.modules.renamer.ConverterHelper;
|
||||
import org.jetbrains.java.decompiler.modules.renamer.IdentifierConverter;
|
||||
import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor;
|
||||
import org.jetbrains.java.decompiler.struct.IDecompiledData;
|
||||
import org.jetbrains.java.decompiler.struct.StructClass;
|
||||
import org.jetbrains.java.decompiler.struct.StructContext;
|
||||
import org.jetbrains.java.decompiler.struct.lazy.LazyLoader;
|
||||
import org.jetbrains.java.decompiler.util.TextBuffer;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class Fernflower implements IDecompiledData {
|
||||
|
||||
private final StructContext structContext;
|
||||
private ClassesProcessor classesProcessor;
|
||||
private final ClassesProcessor classProcessor;
|
||||
private final IIdentifierRenamer helper;
|
||||
private final IdentifierConverter converter;
|
||||
|
||||
public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> customProperties, IFernflowerLogger logger) {
|
||||
Map<String, Object> properties = new HashMap<>(IFernflowerPreferences.DEFAULTS);
|
||||
if (customProperties != null) {
|
||||
properties.putAll(customProperties);
|
||||
}
|
||||
|
||||
String level = (String)properties.get(IFernflowerPreferences.LOG_LEVEL);
|
||||
if (level != null) {
|
||||
try {
|
||||
logger.setSeverity(IFernflowerLogger.Severity.valueOf(level.toUpperCase(Locale.ENGLISH)));
|
||||
}
|
||||
catch (IllegalArgumentException ignore) { }
|
||||
}
|
||||
|
||||
public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> options, IFernflowerLogger logger) {
|
||||
structContext = new StructContext(saver, this, new LazyLoader(provider));
|
||||
DecompilerContext.initContext(options);
|
||||
DecompilerContext.setCounterContainer(new CounterContainer());
|
||||
DecompilerContext.setLogger(logger);
|
||||
classProcessor = new ClassesProcessor(structContext);
|
||||
|
||||
PoolInterceptor interceptor = null;
|
||||
if ("1".equals(properties.get(IFernflowerPreferences.RENAME_ENTITIES))) {
|
||||
helper = loadHelper((String)properties.get(IFernflowerPreferences.USER_RENAMER_CLASS), logger);
|
||||
interceptor = new PoolInterceptor();
|
||||
converter = new IdentifierConverter(structContext, helper, interceptor);
|
||||
}
|
||||
else {
|
||||
helper = null;
|
||||
converter = null;
|
||||
}
|
||||
|
||||
DecompilerContext context = new DecompilerContext(properties, logger, structContext, classProcessor, interceptor);
|
||||
DecompilerContext.setCurrentContext(context);
|
||||
}
|
||||
|
||||
private static IIdentifierRenamer loadHelper(String className, IFernflowerLogger logger) {
|
||||
if (className != null) {
|
||||
try {
|
||||
Class<?> renamerClass = Fernflower.class.getClassLoader().loadClass(className);
|
||||
return (IIdentifierRenamer) renamerClass.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.writeMessage("Cannot load renamer '" + className + "'", IFernflowerLogger.Severity.WARN, e);
|
||||
}
|
||||
}
|
||||
|
||||
return new ConverterHelper();
|
||||
}
|
||||
|
||||
public void addSource(File source) {
|
||||
structContext.addSpace(source, true);
|
||||
}
|
||||
|
||||
public void addLibrary(File library) {
|
||||
structContext.addSpace(library, false);
|
||||
}
|
||||
|
||||
public void decompileContext() {
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) {
|
||||
new IdentifierConverter().rename(structContext);
|
||||
if (converter != null) {
|
||||
converter.rename();
|
||||
}
|
||||
|
||||
classesProcessor = new ClassesProcessor(structContext);
|
||||
|
||||
DecompilerContext.setClassProcessor(classesProcessor);
|
||||
DecompilerContext.setStructContext(structContext);
|
||||
classProcessor.loadClasses(helper);
|
||||
|
||||
structContext.saveContext();
|
||||
}
|
||||
@@ -58,24 +91,18 @@ public class Fernflower implements IDecompiledData {
|
||||
DecompilerContext.setCurrentContext(null);
|
||||
}
|
||||
|
||||
public StructContext getStructContext() {
|
||||
return structContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassEntryName(StructClass cl, String entryName) {
|
||||
ClassNode node = classesProcessor.getMapRootClasses().get(cl.qualifiedName);
|
||||
ClassNode node = classProcessor.getMapRootClasses().get(cl.qualifiedName);
|
||||
if (node.type != ClassNode.CLASS_ROOT) {
|
||||
return null;
|
||||
}
|
||||
else if (converter != null) {
|
||||
String simpleClassName = cl.qualifiedName.substring(cl.qualifiedName.lastIndexOf('/') + 1);
|
||||
return entryName.substring(0, entryName.lastIndexOf('/') + 1) + simpleClassName + ".java";
|
||||
}
|
||||
else {
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) {
|
||||
String simple_classname = cl.qualifiedName.substring(cl.qualifiedName.lastIndexOf('/') + 1);
|
||||
return entryName.substring(0, entryName.lastIndexOf('/') + 1) + simple_classname + ".java";
|
||||
}
|
||||
else {
|
||||
return entryName.substring(0, entryName.lastIndexOf(".class")) + ".java";
|
||||
}
|
||||
return entryName.substring(0, entryName.lastIndexOf(".class")) + ".java";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,12 +111,12 @@ public class Fernflower implements IDecompiledData {
|
||||
try {
|
||||
TextBuffer buffer = new TextBuffer(ClassesProcessor.AVERAGE_CLASS_SIZE);
|
||||
buffer.append(DecompilerContext.getProperty(IFernflowerPreferences.BANNER).toString());
|
||||
classesProcessor.writeClass(cl, buffer);
|
||||
classProcessor.writeClass(cl, buffer);
|
||||
return buffer.toString();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", ex);
|
||||
catch (Throwable t) {
|
||||
DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", t);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
@@ -23,6 +9,7 @@ import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statements;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
|
||||
import org.jetbrains.java.decompiler.struct.StructClass;
|
||||
import org.jetbrains.java.decompiler.struct.StructField;
|
||||
@@ -31,9 +18,7 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class InitializerProcessor {
|
||||
|
||||
public static void extractInitializers(ClassWrapper wrapper) {
|
||||
|
||||
MethodWrapper meth = wrapper.getMethodWrapper(CodeConstants.CLINIT_NAME, "()V");
|
||||
@@ -54,30 +39,26 @@ public class InitializerProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void liftConstructor(ClassWrapper wrapper) {
|
||||
|
||||
for (MethodWrapper meth : wrapper.getMethods()) {
|
||||
if (CodeConstants.INIT_NAME.equals(meth.methodStruct.getName()) && meth.root != null) {
|
||||
Statement firstdata = findFirstData(meth.root);
|
||||
if (firstdata == null) {
|
||||
for (MethodWrapper method : wrapper.getMethods()) {
|
||||
if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) {
|
||||
Statement firstData = Statements.findFirstData(method.root);
|
||||
if (firstData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int index = 0;
|
||||
List<Exprent> lstExprents = firstdata.getExprents();
|
||||
List<Exprent> lstExprents = firstData.getExprents();
|
||||
|
||||
for (Exprent exprent : lstExprents) {
|
||||
|
||||
int action = 0;
|
||||
|
||||
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
|
||||
AssignmentExprent asexpr = (AssignmentExprent)exprent;
|
||||
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
|
||||
FieldExprent fexpr = (FieldExprent)asexpr.getLeft();
|
||||
if (fexpr.getClassname().equals(wrapper.getClassStruct().qualifiedName)) {
|
||||
StructField structField = wrapper.getClassStruct().getField(fexpr.getName(), fexpr.getDescriptor().descriptorString);
|
||||
AssignmentExprent assignExpr = (AssignmentExprent)exprent;
|
||||
if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD && assignExpr.getRight().type == Exprent.EXPRENT_VAR) {
|
||||
FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();
|
||||
if (fExpr.getClassname().equals(wrapper.getClassStruct().qualifiedName)) {
|
||||
StructField structField = wrapper.getClassStruct().getField(fExpr.getName(), fExpr.getDescriptor().descriptorString);
|
||||
if (structField != null && structField.hasModifier(CodeConstants.ACC_FINAL)) {
|
||||
action = 1;
|
||||
}
|
||||
@@ -85,7 +66,7 @@ public class InitializerProcessor {
|
||||
}
|
||||
}
|
||||
else if (index > 0 && exprent.type == Exprent.EXPRENT_INVOCATION &&
|
||||
isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, true)) {
|
||||
Statements.isInvocationInitConstructor((InvocationExprent)exprent, method, wrapper, true)) {
|
||||
// this() or super()
|
||||
lstExprents.add(0, lstExprents.remove(index));
|
||||
action = 2;
|
||||
@@ -101,52 +82,50 @@ public class InitializerProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void hideEmptySuper(ClassWrapper wrapper) {
|
||||
|
||||
for (MethodWrapper meth : wrapper.getMethods()) {
|
||||
if (CodeConstants.INIT_NAME.equals(meth.methodStruct.getName()) && meth.root != null) {
|
||||
Statement firstdata = findFirstData(meth.root);
|
||||
if (firstdata == null || firstdata.getExprents().isEmpty()) {
|
||||
for (MethodWrapper method : wrapper.getMethods()) {
|
||||
if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) {
|
||||
Statement firstData = Statements.findFirstData(method.root);
|
||||
if (firstData == null || firstData.getExprents().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Exprent exprent = firstdata.getExprents().get(0);
|
||||
Exprent exprent = firstData.getExprents().get(0);
|
||||
if (exprent.type == Exprent.EXPRENT_INVOCATION) {
|
||||
InvocationExprent invexpr = (InvocationExprent)exprent;
|
||||
if (isInvocationInitConstructor(invexpr, meth, wrapper, false) && invexpr.getLstParameters().isEmpty()) {
|
||||
firstdata.getExprents().remove(0);
|
||||
InvocationExprent invExpr = (InvocationExprent)exprent;
|
||||
if (Statements.isInvocationInitConstructor(invExpr, method, wrapper, false) && invExpr.getLstParameters().isEmpty()) {
|
||||
firstData.getExprents().remove(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void extractStaticInitializers(ClassWrapper wrapper, MethodWrapper meth) {
|
||||
|
||||
RootStatement root = meth.root;
|
||||
private static void extractStaticInitializers(ClassWrapper wrapper, MethodWrapper method) {
|
||||
RootStatement root = method.root;
|
||||
StructClass cl = wrapper.getClassStruct();
|
||||
Statement firstData = Statements.findFirstData(root);
|
||||
if (firstData != null) {
|
||||
boolean inlineInitializers = cl.hasModifier(CodeConstants.ACC_INTERFACE) || cl.hasModifier(CodeConstants.ACC_ENUM);
|
||||
|
||||
Statement firstdata = findFirstData(root);
|
||||
if (firstdata != null) {
|
||||
while (!firstdata.getExprents().isEmpty()) {
|
||||
Exprent exprent = firstdata.getExprents().get(0);
|
||||
while (!firstData.getExprents().isEmpty()) {
|
||||
Exprent exprent = firstData.getExprents().get(0);
|
||||
|
||||
boolean found = false;
|
||||
|
||||
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
|
||||
AssignmentExprent asexpr = (AssignmentExprent)exprent;
|
||||
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) {
|
||||
FieldExprent fexpr = (FieldExprent)asexpr.getLeft();
|
||||
if (fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) &&
|
||||
cl.hasField(fexpr.getName(), fexpr.getDescriptor().descriptorString)) {
|
||||
AssignmentExprent assignExpr = (AssignmentExprent)exprent;
|
||||
if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) {
|
||||
FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();
|
||||
if (fExpr.isStatic() && fExpr.getClassname().equals(cl.qualifiedName) &&
|
||||
cl.hasField(fExpr.getName(), fExpr.getDescriptor().descriptorString)) {
|
||||
|
||||
if (isExprentIndependent(asexpr.getRight(), meth)) {
|
||||
|
||||
String keyField = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString);
|
||||
// interfaces fields should always be initialized inline
|
||||
if (inlineInitializers || isExprentIndependent(assignExpr.getRight(), method)) {
|
||||
String keyField = InterpreterUtil.makeUniqueKey(fExpr.getName(), fExpr.getDescriptor().descriptorString);
|
||||
if (!wrapper.getStaticFieldInitializers().containsKey(keyField)) {
|
||||
wrapper.getStaticFieldInitializers().addWithKey(asexpr.getRight(), keyField);
|
||||
firstdata.getExprents().remove(0);
|
||||
wrapper.getStaticFieldInitializers().addWithKey(assignExpr.getRight(), keyField);
|
||||
firstData.getExprents().remove(0);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
@@ -162,27 +141,26 @@ public class InitializerProcessor {
|
||||
}
|
||||
|
||||
private static void extractDynamicInitializers(ClassWrapper wrapper) {
|
||||
|
||||
StructClass cl = wrapper.getClassStruct();
|
||||
|
||||
boolean isAnonymous = DecompilerContext.getClassProcessor().getMapRootClasses().get(cl.qualifiedName).type == ClassNode.CLASS_ANONYMOUS;
|
||||
|
||||
List<List<Exprent>> lstFirst = new ArrayList<List<Exprent>>();
|
||||
List<MethodWrapper> lstMethWrappers = new ArrayList<MethodWrapper>();
|
||||
List<List<Exprent>> lstFirst = new ArrayList<>();
|
||||
List<MethodWrapper> lstMethodWrappers = new ArrayList<>();
|
||||
|
||||
for (MethodWrapper meth : wrapper.getMethods()) {
|
||||
if (CodeConstants.INIT_NAME.equals(meth.methodStruct.getName()) && meth.root != null) { // successfully decompiled constructor
|
||||
Statement firstdata = findFirstData(meth.root);
|
||||
if (firstdata == null || firstdata.getExprents().isEmpty()) {
|
||||
for (MethodWrapper method : wrapper.getMethods()) {
|
||||
if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) { // successfully decompiled constructor
|
||||
Statement firstData = Statements.findFirstData(method.root);
|
||||
if (firstData == null || firstData.getExprents().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
lstFirst.add(firstdata.getExprents());
|
||||
lstMethWrappers.add(meth);
|
||||
lstFirst.add(firstData.getExprents());
|
||||
lstMethodWrappers.add(method);
|
||||
|
||||
Exprent exprent = firstdata.getExprents().get(0);
|
||||
Exprent exprent = firstData.getExprents().get(0);
|
||||
if (!isAnonymous) { // FIXME: doesn't make sense
|
||||
if (exprent.type != Exprent.EXPRENT_INVOCATION ||
|
||||
!isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, false)) {
|
||||
!Statements.isInvocationInitConstructor((InvocationExprent)exprent, method, wrapper, false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -194,12 +172,10 @@ public class InitializerProcessor {
|
||||
}
|
||||
|
||||
while (true) {
|
||||
|
||||
String fieldWithDescr = null;
|
||||
Exprent value = null;
|
||||
|
||||
for (int i = 0; i < lstFirst.size(); i++) {
|
||||
|
||||
List<Exprent> lst = lstFirst.get(i);
|
||||
|
||||
if (lst.size() < (isAnonymous ? 1 : 2)) {
|
||||
@@ -211,22 +187,21 @@ public class InitializerProcessor {
|
||||
boolean found = false;
|
||||
|
||||
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
|
||||
AssignmentExprent asexpr = (AssignmentExprent)exprent;
|
||||
if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) {
|
||||
FieldExprent fexpr = (FieldExprent)asexpr.getLeft();
|
||||
if (!fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) &&
|
||||
cl.hasField(fexpr.getName(), fexpr
|
||||
.getDescriptor().descriptorString)) { // check for the physical existence of the field. Could be defined in a superclass.
|
||||
AssignmentExprent assignExpr = (AssignmentExprent)exprent;
|
||||
if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) {
|
||||
FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();
|
||||
if (!fExpr.isStatic() && fExpr.getClassname().equals(cl.qualifiedName) &&
|
||||
cl.hasField(fExpr.getName(), fExpr.getDescriptor().descriptorString)) { // check for the physical existence of the field. Could be defined in a superclass.
|
||||
|
||||
if (isExprentIndependent(asexpr.getRight(), lstMethWrappers.get(i))) {
|
||||
String fieldKey = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString);
|
||||
if (isExprentIndependent(assignExpr.getRight(), lstMethodWrappers.get(i))) {
|
||||
String fieldKey = InterpreterUtil.makeUniqueKey(fExpr.getName(), fExpr.getDescriptor().descriptorString);
|
||||
if (fieldWithDescr == null) {
|
||||
fieldWithDescr = fieldKey;
|
||||
value = asexpr.getRight();
|
||||
value = assignExpr.getRight();
|
||||
}
|
||||
else {
|
||||
if (!fieldWithDescr.equals(fieldKey) ||
|
||||
!value.equals(asexpr.getRight())) {
|
||||
!value.equals(assignExpr.getRight())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -254,19 +229,17 @@ public class InitializerProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isExprentIndependent(Exprent exprent, MethodWrapper meth) {
|
||||
|
||||
private static boolean isExprentIndependent(Exprent exprent, MethodWrapper method) {
|
||||
List<Exprent> lst = exprent.getAllExprents(true);
|
||||
lst.add(exprent);
|
||||
|
||||
for (Exprent expr : lst) {
|
||||
switch (expr.type) {
|
||||
case Exprent.EXPRENT_VAR:
|
||||
VarVersionPair varpaar = new VarVersionPair((VarExprent)expr);
|
||||
if (!meth.varproc.getExternalVars().contains(varpaar)) {
|
||||
String varname = meth.varproc.getVarName(varpaar);
|
||||
|
||||
if (!varname.equals("this") && !varname.endsWith(".this")) { // FIXME: remove direct comparison with strings
|
||||
VarVersionPair varPair = new VarVersionPair((VarExprent)expr);
|
||||
if (!method.varproc.getExternalVars().contains(varPair)) {
|
||||
String varName = method.varproc.getVarName(varPair);
|
||||
if (!varName.equals("this") && !varName.endsWith(".this")) { // FIXME: remove direct comparison with strings
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -278,48 +251,4 @@ public class InitializerProcessor {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private static Statement findFirstData(Statement stat) {
|
||||
|
||||
if (stat.getExprents() != null) {
|
||||
return stat;
|
||||
}
|
||||
else {
|
||||
if (stat.isLabeled()) { // FIXME: Why??
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (stat.type) {
|
||||
case Statement.TYPE_SEQUENCE:
|
||||
case Statement.TYPE_IF:
|
||||
case Statement.TYPE_ROOT:
|
||||
case Statement.TYPE_SWITCH:
|
||||
case Statement.TYPE_SYNCRONIZED:
|
||||
return findFirstData(stat.getFirst());
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isInvocationInitConstructor(InvocationExprent inv, MethodWrapper meth, ClassWrapper wrapper, boolean withThis) {
|
||||
|
||||
if (inv.getFunctype() == InvocationExprent.TYP_INIT) {
|
||||
if (inv.getInstance().type == Exprent.EXPRENT_VAR) {
|
||||
VarExprent instvar = (VarExprent)inv.getInstance();
|
||||
VarVersionPair varpaar = new VarVersionPair(instvar);
|
||||
|
||||
String classname = meth.varproc.getThisVars().get(varpaar);
|
||||
|
||||
if (classname != null) { // any this instance. TODO: Restrict to current class?
|
||||
if (withThis || !wrapper.getClassStruct().qualifiedName.equals(inv.getClassname())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.collectors;
|
||||
|
||||
import org.jetbrains.java.decompiler.struct.attr.StructLineNumberTableAttribute;
|
||||
@@ -21,13 +7,11 @@ import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class BytecodeMappingTracer {
|
||||
public static final BytecodeMappingTracer DUMMY = new BytecodeMappingTracer();
|
||||
|
||||
private int currentSourceLine;
|
||||
|
||||
private StructLineNumberTableAttribute lineNumberTable = null;
|
||||
|
||||
// bytecode offset, source line
|
||||
private final Map<Integer, Integer> mapping = new HashMap<Integer, Integer>();
|
||||
private final Map<Integer, Integer> mapping = new HashMap<>(); // bytecode offset, source line
|
||||
|
||||
public BytecodeMappingTracer() { }
|
||||
|
||||
@@ -43,16 +27,8 @@ public class BytecodeMappingTracer {
|
||||
currentSourceLine += number_lines;
|
||||
}
|
||||
|
||||
public void shiftSourceLines(int shift) {
|
||||
for (Entry<Integer, Integer> entry : mapping.entrySet()) {
|
||||
entry.setValue(entry.getValue() + shift);
|
||||
}
|
||||
}
|
||||
|
||||
public void addMapping(int bytecode_offset) {
|
||||
if (!mapping.containsKey(bytecode_offset)) {
|
||||
mapping.put(bytecode_offset, currentSourceLine);
|
||||
}
|
||||
mapping.putIfAbsent(bytecode_offset, currentSourceLine);
|
||||
}
|
||||
|
||||
public void addMapping(Set<Integer> bytecode_offsets) {
|
||||
@@ -66,9 +42,7 @@ public class BytecodeMappingTracer {
|
||||
public void addTracer(BytecodeMappingTracer tracer) {
|
||||
if (tracer != null) {
|
||||
for (Entry<Integer, Integer> entry : tracer.mapping.entrySet()) {
|
||||
if (!mapping.containsKey(entry.getKey())) {
|
||||
mapping.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
mapping.putIfAbsent(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,7 +63,7 @@ public class BytecodeMappingTracer {
|
||||
this.lineNumberTable = lineNumberTable;
|
||||
}
|
||||
|
||||
private final Set<Integer> unmappedLines = new HashSet<Integer>();
|
||||
private final Set<Integer> unmappedLines = new HashSet<>();
|
||||
|
||||
public Set<Integer> getUnmappedLines() {
|
||||
return unmappedLines;
|
||||
@@ -100,7 +74,7 @@ public class BytecodeMappingTracer {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Map<Integer, Integer> res = new HashMap<Integer, Integer>();
|
||||
Map<Integer, Integer> res = new HashMap<>();
|
||||
|
||||
// first match offsets from line number table
|
||||
int[] data = lineNumberTable.getRawData();
|
||||
@@ -126,4 +100,4 @@ public class BytecodeMappingTracer {
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +1,28 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.collectors;
|
||||
|
||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||
import org.jetbrains.java.decompiler.main.TextBuffer;
|
||||
import org.jetbrains.java.decompiler.util.TextBuffer;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class BytecodeSourceMapper {
|
||||
|
||||
private int offset_total;
|
||||
|
||||
// class, method, bytecode offset, source line
|
||||
private final Map<String, Map<String, Map<Integer, Integer>>> mapping = new LinkedHashMap<String, Map<String, Map<Integer, Integer>>>();
|
||||
private final Map<String, Map<String, Map<Integer, Integer>>> mapping = new LinkedHashMap<>();
|
||||
|
||||
// original line to decompiled line
|
||||
private final Map<Integer, Integer> linesMapping = new HashMap<Integer, Integer>();
|
||||
private final Set<Integer> unmappedLines = new TreeSet<Integer>();
|
||||
private final Map<Integer, Integer> linesMapping = new HashMap<>();
|
||||
private final Set<Integer> unmappedLines = new TreeSet<>();
|
||||
|
||||
public void addMapping(String className, String methodName, int bytecodeOffset, int sourceLine) {
|
||||
Map<String, Map<Integer, Integer>> class_mapping = mapping.get(className);
|
||||
if (class_mapping == null) {
|
||||
mapping.put(className, class_mapping = new LinkedHashMap<String, Map<Integer, Integer>>()); // need to preserve order
|
||||
}
|
||||
|
||||
Map<Integer, Integer> method_mapping = class_mapping.get(methodName);
|
||||
if (method_mapping == null) {
|
||||
class_mapping.put(methodName, method_mapping = new HashMap<Integer, Integer>());
|
||||
}
|
||||
Map<String, Map<Integer, Integer>> class_mapping = mapping.computeIfAbsent(className, k -> new LinkedHashMap<>()); // need to preserve order
|
||||
Map<Integer, Integer> method_mapping = class_mapping.computeIfAbsent(methodName, k -> new HashMap<>());
|
||||
|
||||
// don't overwrite
|
||||
if (!method_mapping.containsKey(bytecodeOffset)) {
|
||||
method_mapping.put(bytecodeOffset, sourceLine);
|
||||
}
|
||||
method_mapping.putIfAbsent(bytecodeOffset, sourceLine);
|
||||
}
|
||||
|
||||
public void addTracer(String className, String methodName, BytecodeMappingTracer tracer) {
|
||||
@@ -78,7 +54,7 @@ public class BytecodeSourceMapper {
|
||||
|
||||
buffer.appendIndent(1).append("method '" + method_entry.getKey() + "' {" + lineSeparator);
|
||||
|
||||
List<Integer> lstBytecodeOffsets = new ArrayList<Integer>(method_mapping.keySet());
|
||||
List<Integer> lstBytecodeOffsets = new ArrayList<>(method_mapping.keySet());
|
||||
Collections.sort(lstBytecodeOffsets);
|
||||
|
||||
for (Integer offset : lstBytecodeOffsets) {
|
||||
@@ -97,7 +73,7 @@ public class BytecodeSourceMapper {
|
||||
|
||||
// lines mapping
|
||||
buffer.append("Lines mapping:").appendLineSeparator();
|
||||
Map<Integer, Integer> sorted = new TreeMap<Integer, Integer>(linesMapping);
|
||||
Map<Integer, Integer> sorted = new TreeMap<>(linesMapping);
|
||||
for (Entry<Integer, Integer> entry : sorted.entrySet()) {
|
||||
buffer.append(entry.getKey()).append(" <-> ").append(entry.getValue() + offset_total + 1).appendLineSeparator();
|
||||
}
|
||||
@@ -112,14 +88,6 @@ public class BytecodeSourceMapper {
|
||||
}
|
||||
}
|
||||
|
||||
public int getTotalOffset() {
|
||||
return offset_total;
|
||||
}
|
||||
|
||||
public void setTotalOffset(int offset_total) {
|
||||
this.offset_total = offset_total;
|
||||
}
|
||||
|
||||
public void addTotalOffset(int offset_total) {
|
||||
this.offset_total += offset_total;
|
||||
}
|
||||
@@ -138,4 +106,4 @@ public class BytecodeSourceMapper {
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,9 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.collectors;
|
||||
|
||||
public class CounterContainer {
|
||||
|
||||
public static final int STATEMENT_COUNTER = 0;
|
||||
public static final int EXPRENT_COUNTER = 1;
|
||||
public static final int EXPRESSION_COUNTER = 1;
|
||||
public static final int VAR_COUNTER = 2;
|
||||
|
||||
private final int[] values = new int[]{1, 1, 1};
|
||||
@@ -34,4 +19,4 @@ public class CounterContainer {
|
||||
public int getCounterAndIncrement(int counter) {
|
||||
return values[counter]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +1,184 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.collectors;
|
||||
|
||||
import org.jetbrains.java.decompiler.main.ClassesProcessor;
|
||||
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
|
||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||
import org.jetbrains.java.decompiler.main.TextBuffer;
|
||||
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
|
||||
import org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute;
|
||||
import org.jetbrains.java.decompiler.util.TextBuffer;
|
||||
import org.jetbrains.java.decompiler.struct.StructClass;
|
||||
import org.jetbrains.java.decompiler.struct.StructContext;
|
||||
import org.jetbrains.java.decompiler.struct.StructField;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ImportCollector {
|
||||
|
||||
private static final String JAVA_LANG_PACKAGE = "java.lang";
|
||||
|
||||
private final Map<String, String> mapSimpleNames = new HashMap<String, String>();
|
||||
private final Set<String> setNotImportedNames = new HashSet<String>();
|
||||
private String currentPackageSlash = "";
|
||||
private String currentPackagePoint = "";
|
||||
private final Map<String, String> mapSimpleNames = new HashMap<>();
|
||||
private final Set<String> setNotImportedNames = new HashSet<>();
|
||||
// set of field names in this class and all its predecessors.
|
||||
private final Set<String> setFieldNames = new HashSet<>();
|
||||
private final Set<String> setInnerClassNames = new HashSet<>();
|
||||
private final String currentPackageSlash;
|
||||
private final String currentPackagePoint;
|
||||
|
||||
public ImportCollector(ClassNode root) {
|
||||
|
||||
String clname = root.classStruct.qualifiedName;
|
||||
int index = clname.lastIndexOf("/");
|
||||
String clName = root.classStruct.qualifiedName;
|
||||
int index = clName.lastIndexOf('/');
|
||||
if (index >= 0) {
|
||||
currentPackageSlash = clname.substring(0, index);
|
||||
currentPackagePoint = currentPackageSlash.replace('/', '.');
|
||||
currentPackageSlash += "/";
|
||||
String packageName = clName.substring(0, index);
|
||||
currentPackageSlash = packageName + '/';
|
||||
currentPackagePoint = packageName.replace('/', '.');
|
||||
}
|
||||
else {
|
||||
currentPackageSlash = "";
|
||||
currentPackagePoint = "";
|
||||
}
|
||||
|
||||
Map<String, StructClass> classes = DecompilerContext.getStructContext().getClasses();
|
||||
LinkedList<String> queue = new LinkedList<>();
|
||||
StructClass currentClass = root.classStruct;
|
||||
while (currentClass != null) {
|
||||
if (currentClass.superClass != null) {
|
||||
queue.add(currentClass.superClass.getString());
|
||||
}
|
||||
|
||||
Collections.addAll(queue, currentClass.getInterfaceNames());
|
||||
|
||||
// all field names for the current class ..
|
||||
for (StructField f : currentClass.getFields()) {
|
||||
setFieldNames.add(f.getName());
|
||||
}
|
||||
|
||||
// .. all inner classes for the current class ..
|
||||
StructInnerClassesAttribute attribute = currentClass.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES);
|
||||
if (attribute != null) {
|
||||
for (StructInnerClassesAttribute.Entry entry : attribute.getEntries()) {
|
||||
if (entry.enclosingName != null && entry.enclosingName.equals(currentClass.qualifiedName)) {
|
||||
setInnerClassNames.add(entry.simpleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .. and traverse through parent.
|
||||
currentClass = !queue.isEmpty() ? classes.get(queue.removeFirst()) : null;
|
||||
while (currentClass == null && !queue.isEmpty()) {
|
||||
currentClass = classes.get(queue.removeFirst());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getShortName(String fullname) {
|
||||
return getShortName(fullname, true);
|
||||
/**
|
||||
* Check whether the package-less name ClassName is shaded by variable in a context of
|
||||
* the decompiled class
|
||||
* @param classToName - pkg.name.ClassName - class to find shortname for
|
||||
* @return ClassName if the name is not shaded by local field, pkg.name.ClassName otherwise
|
||||
*/
|
||||
public String getShortNameInClassContext(String classToName) {
|
||||
String shortName = getShortName(classToName);
|
||||
if (setFieldNames.contains(shortName)) {
|
||||
return classToName;
|
||||
}
|
||||
else {
|
||||
return shortName;
|
||||
}
|
||||
}
|
||||
|
||||
public String getShortName(String fullname, boolean imported) {
|
||||
public String getShortName(String fullName) {
|
||||
return getShortName(fullName, true);
|
||||
}
|
||||
|
||||
ClassesProcessor clproc = DecompilerContext.getClassProcessor();
|
||||
ClassNode node = clproc.getMapRootClasses().get(fullname.replace('.', '/'));
|
||||
|
||||
String retname = null;
|
||||
public String getShortName(String fullName, boolean imported) {
|
||||
ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(fullName.replace('.', '/')); //todo[r.sh] anonymous classes?
|
||||
|
||||
String result = null;
|
||||
if (node != null && node.classStruct.isOwn()) {
|
||||
|
||||
retname = node.simpleName;
|
||||
result = node.simpleName;
|
||||
|
||||
while (node.parent != null && node.type == ClassNode.CLASS_MEMBER) {
|
||||
retname = node.parent.simpleName + "." + retname;
|
||||
//noinspection StringConcatenationInLoop
|
||||
result = node.parent.simpleName + '.' + result;
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (node.type == ClassNode.CLASS_ROOT) {
|
||||
fullname = node.classStruct.qualifiedName;
|
||||
fullname = fullname.replace('/', '.');
|
||||
fullName = node.classStruct.qualifiedName;
|
||||
fullName = fullName.replace('/', '.');
|
||||
}
|
||||
else {
|
||||
return retname;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
fullname = fullname.replace('$', '.');
|
||||
fullName = fullName.replace('$', '.');
|
||||
}
|
||||
|
||||
String nshort = fullname;
|
||||
String npackage = "";
|
||||
String shortName = fullName;
|
||||
String packageName = "";
|
||||
|
||||
int lastpoint = fullname.lastIndexOf(".");
|
||||
|
||||
if (lastpoint >= 0) {
|
||||
nshort = fullname.substring(lastpoint + 1);
|
||||
npackage = fullname.substring(0, lastpoint);
|
||||
int lastDot = fullName.lastIndexOf('.');
|
||||
if (lastDot >= 0) {
|
||||
shortName = fullName.substring(lastDot + 1);
|
||||
packageName = fullName.substring(0, lastDot);
|
||||
}
|
||||
|
||||
StructContext context = DecompilerContext.getStructContext();
|
||||
|
||||
// check for another class which could 'shadow' this one. Two cases:
|
||||
// check for another class which could 'shadow' this one. Three cases:
|
||||
// 1) class with the same short name in the current package
|
||||
// 2) class with the same short name in the default package
|
||||
boolean existsDefaultClass = (context.getClass(currentPackageSlash + nshort) != null
|
||||
&& !npackage.equals(currentPackagePoint)) // current package
|
||||
|| (context.getClass(nshort) != null
|
||||
&& !currentPackagePoint.isEmpty()); // default package
|
||||
// 3) inner class with the same short name in the current class, a super class, or an implemented interface
|
||||
boolean existsDefaultClass =
|
||||
(context.getClass(currentPackageSlash + shortName) != null && !packageName.equals(currentPackagePoint)) || // current package
|
||||
(context.getClass(shortName) != null && !currentPackagePoint.isEmpty()) || // default package
|
||||
setInnerClassNames.contains(shortName); // inner class
|
||||
|
||||
if (existsDefaultClass ||
|
||||
(mapSimpleNames.containsKey(nshort) && !npackage.equals(mapSimpleNames.get(nshort)))) {
|
||||
return fullname;
|
||||
(mapSimpleNames.containsKey(shortName) && !packageName.equals(mapSimpleNames.get(shortName)))) {
|
||||
// don't return full name because if the class is a inner class, full name refers to the parent full name, not the child full name
|
||||
return result == null ? fullName : (packageName + "." + result);
|
||||
}
|
||||
else if (!mapSimpleNames.containsKey(nshort)) {
|
||||
mapSimpleNames.put(nshort, npackage);
|
||||
|
||||
else if (!mapSimpleNames.containsKey(shortName)) {
|
||||
mapSimpleNames.put(shortName, packageName);
|
||||
if (!imported) {
|
||||
setNotImportedNames.add(nshort);
|
||||
setNotImportedNames.add(shortName);
|
||||
}
|
||||
}
|
||||
|
||||
return retname == null ? nshort : retname;
|
||||
return result == null ? shortName : result;
|
||||
}
|
||||
|
||||
public int writeImports(TextBuffer buffer) {
|
||||
int importlines_written = 0;
|
||||
int importLinesWritten = 0;
|
||||
|
||||
List<String> imports = packImports();
|
||||
|
||||
for (String s : imports) {
|
||||
buffer.append("import ");
|
||||
buffer.append(s);
|
||||
buffer.append(";");
|
||||
buffer.append(';');
|
||||
buffer.appendLineSeparator();
|
||||
|
||||
importlines_written++;
|
||||
importLinesWritten++;
|
||||
}
|
||||
|
||||
return importlines_written;
|
||||
return importLinesWritten;
|
||||
}
|
||||
|
||||
private List<String> packImports() {
|
||||
List<Entry<String, String>> lst = new ArrayList<Entry<String, String>>(mapSimpleNames.entrySet());
|
||||
|
||||
Collections.sort(lst, new Comparator<Entry<String, String>>() {
|
||||
public int compare(Entry<String, String> par0, Entry<String, String> par1) {
|
||||
int res = par0.getValue().compareTo(par1.getValue());
|
||||
if (res == 0) {
|
||||
res = par0.getKey().compareTo(par1.getKey());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
});
|
||||
|
||||
List<String> res = new ArrayList<String>();
|
||||
for (Entry<String, String> ent : lst) {
|
||||
// exclude a current class or one of the nested ones, java.lang and empty packages
|
||||
if (!setNotImportedNames.contains(ent.getKey()) &&
|
||||
!JAVA_LANG_PACKAGE.equals(ent.getValue()) &&
|
||||
!ent.getValue().isEmpty()) {
|
||||
res.add(ent.getValue() + "." + ent.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
return mapSimpleNames.entrySet().stream()
|
||||
.filter(ent ->
|
||||
// exclude the current class or one of the nested ones
|
||||
// empty, java.lang and the current packages
|
||||
!setNotImportedNames.contains(ent.getKey()) &&
|
||||
!ent.getValue().isEmpty() &&
|
||||
!JAVA_LANG_PACKAGE.equals(ent.getValue()) &&
|
||||
!ent.getValue().equals(currentPackagePoint)
|
||||
)
|
||||
.sorted(Map.Entry.<String, String>comparingByValue().thenComparing(Map.Entry.comparingByKey()))
|
||||
.map(ent -> ent.getValue() + "." + ent.getKey())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,17 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.collectors;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class VarNamesCollector {
|
||||
|
||||
private final Set<String> usedNames = new HashSet<String>();
|
||||
private final Set<String> usedNames = new HashSet<>();
|
||||
|
||||
public VarNamesCollector() { }
|
||||
|
||||
public VarNamesCollector(Set<String> setNames) {
|
||||
public VarNamesCollector(Collection<String> setNames) {
|
||||
usedNames.addAll(setNames);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.main.Fernflower;
|
||||
@@ -21,27 +7,41 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
|
||||
import org.jetbrains.java.decompiler.main.extern.IResultSaver;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class BaseDecompiler {
|
||||
|
||||
private final Fernflower fernflower;
|
||||
private final Fernflower engine;
|
||||
|
||||
public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> options, IFernflowerLogger logger) {
|
||||
fernflower = new Fernflower(provider, saver, options, logger);
|
||||
engine = new Fernflower(provider, saver, options, logger);
|
||||
}
|
||||
|
||||
public void addSpace(File file, boolean isOwn) throws IOException {
|
||||
fernflower.getStructContext().addSpace(file, isOwn);
|
||||
public void addSource(File source) {
|
||||
engine.addSource(source);
|
||||
}
|
||||
|
||||
public void addLibrary(File library) {
|
||||
engine.addLibrary(library);
|
||||
}
|
||||
|
||||
/** @deprecated use {@link #addSource(File)} / {@link #addLibrary(File)} instead */
|
||||
@Deprecated
|
||||
public void addSpace(File file, boolean own) {
|
||||
if (own) {
|
||||
addSource(file);
|
||||
}
|
||||
else {
|
||||
addLibrary(file);
|
||||
}
|
||||
}
|
||||
|
||||
public void decompileContext() {
|
||||
try {
|
||||
fernflower.decompileContext();
|
||||
engine.decompileContext();
|
||||
}
|
||||
finally {
|
||||
fernflower.clearContext();
|
||||
engine.clearContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||
@@ -23,6 +9,7 @@ import org.jetbrains.java.decompiler.main.extern.IResultSaver;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
@@ -31,7 +18,6 @@ import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
|
||||
|
||||
@SuppressWarnings("UseOfSystemOutOrSystemErr")
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 2) {
|
||||
@@ -41,9 +27,9 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> mapOptions = new HashMap<String, Object>();
|
||||
List<File> lstSources = new ArrayList<File>();
|
||||
List<File> lstLibraries = new ArrayList<File>();
|
||||
Map<String, Object> mapOptions = new HashMap<>();
|
||||
List<File> sources = new ArrayList<>();
|
||||
List<File> libraries = new ArrayList<>();
|
||||
|
||||
boolean isOption = true;
|
||||
for (int i = 0; i < args.length - 1; ++i) { // last parameter - destination
|
||||
@@ -64,15 +50,15 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
|
||||
isOption = false;
|
||||
|
||||
if (arg.startsWith("-e=")) {
|
||||
addPath(lstLibraries, arg.substring(3));
|
||||
addPath(libraries, arg.substring(3));
|
||||
}
|
||||
else {
|
||||
addPath(lstSources, arg);
|
||||
addPath(sources, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lstSources.isEmpty()) {
|
||||
if (sources.isEmpty()) {
|
||||
System.out.println("error: no sources given");
|
||||
return;
|
||||
}
|
||||
@@ -86,18 +72,18 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
|
||||
PrintStreamLogger logger = new PrintStreamLogger(System.out);
|
||||
ConsoleDecompiler decompiler = new ConsoleDecompiler(destination, mapOptions, logger);
|
||||
|
||||
for (File source : lstSources) {
|
||||
decompiler.addSpace(source, true);
|
||||
for (File source : sources) {
|
||||
decompiler.addSource(source);
|
||||
}
|
||||
for (File library : lstLibraries) {
|
||||
decompiler.addSpace(library, false);
|
||||
for (File library : libraries) {
|
||||
decompiler.addLibrary(library);
|
||||
}
|
||||
|
||||
decompiler.decompileContext();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UseOfSystemOutOrSystemErr")
|
||||
private static void addPath(List<File> list, String path) {
|
||||
private static void addPath(List<? super File> list, String path) {
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
list.add(file);
|
||||
@@ -112,30 +98,29 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
|
||||
// *******************************************************************
|
||||
|
||||
private final File root;
|
||||
private final Fernflower fernflower;
|
||||
private final Map<String, ZipOutputStream> mapArchiveStreams = new HashMap<String, ZipOutputStream>();
|
||||
private final Map<String, Set<String>> mapArchiveEntries = new HashMap<String, Set<String>>();
|
||||
|
||||
@SuppressWarnings("UseOfSystemOutOrSystemErr")
|
||||
public ConsoleDecompiler(File destination, Map<String, Object> options) {
|
||||
this(destination, options, new PrintStreamLogger(System.out));
|
||||
}
|
||||
private final Fernflower engine;
|
||||
private final Map<String, ZipOutputStream> mapArchiveStreams = new HashMap<>();
|
||||
private final Map<String, Set<String>> mapArchiveEntries = new HashMap<>();
|
||||
|
||||
protected ConsoleDecompiler(File destination, Map<String, Object> options, IFernflowerLogger logger) {
|
||||
root = destination;
|
||||
fernflower = new Fernflower(this, this, options, logger);
|
||||
engine = new Fernflower(this, this, options, logger);
|
||||
}
|
||||
|
||||
public void addSpace(File file, boolean isOwn) {
|
||||
fernflower.getStructContext().addSpace(file, isOwn);
|
||||
public void addSource(File source) {
|
||||
engine.addSource(source);
|
||||
}
|
||||
|
||||
public void addLibrary(File library) {
|
||||
engine.addLibrary(library);
|
||||
}
|
||||
|
||||
public void decompileContext() {
|
||||
try {
|
||||
fernflower.decompileContext();
|
||||
engine.decompileContext();
|
||||
}
|
||||
finally {
|
||||
fernflower.clearContext();
|
||||
engine.clearContext();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,17 +135,11 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
|
||||
return InterpreterUtil.getBytes(file);
|
||||
}
|
||||
else {
|
||||
ZipFile archive = new ZipFile(file);
|
||||
try {
|
||||
try (ZipFile archive = new ZipFile(file)) {
|
||||
ZipEntry entry = archive.getEntry(internalPath);
|
||||
if (entry == null) {
|
||||
throw new IOException("Entry not found: " + internalPath);
|
||||
}
|
||||
if (entry == null) throw new IOException("Entry not found: " + internalPath);
|
||||
return InterpreterUtil.getBytes(archive, entry);
|
||||
}
|
||||
finally {
|
||||
archive.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,14 +172,8 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
|
||||
@Override
|
||||
public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) {
|
||||
File file = new File(getAbsolutePath(path), entryName);
|
||||
try {
|
||||
Writer out = new OutputStreamWriter(new FileOutputStream(file), "UTF8");
|
||||
try {
|
||||
out.write(content);
|
||||
}
|
||||
finally {
|
||||
out.close();
|
||||
}
|
||||
try (Writer out = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
|
||||
out.write(content);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
DecompilerContext.getLogger().writeMessage("Cannot write class file " + file, ex);
|
||||
@@ -216,7 +189,6 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
|
||||
}
|
||||
|
||||
FileOutputStream fileStream = new FileOutputStream(file);
|
||||
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
|
||||
ZipOutputStream zipStream = manifest != null ? new JarOutputStream(fileStream, manifest) : new ZipOutputStream(fileStream);
|
||||
mapArchiveStreams.put(file.getPath(), zipStream);
|
||||
}
|
||||
@@ -238,21 +210,15 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ZipFile srcArchive = new ZipFile(new File(source));
|
||||
try {
|
||||
ZipEntry entry = srcArchive.getEntry(entryName);
|
||||
if (entry != null) {
|
||||
InputStream in = srcArchive.getInputStream(entry);
|
||||
try (ZipFile srcArchive = new ZipFile(new File(source))) {
|
||||
ZipEntry entry = srcArchive.getEntry(entryName);
|
||||
if (entry != null) {
|
||||
try (InputStream in = srcArchive.getInputStream(entry)) {
|
||||
ZipOutputStream out = mapArchiveStreams.get(file);
|
||||
out.putNextEntry(new ZipEntry(entryName));
|
||||
InterpreterUtil.copyStream(in, out);
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
srcArchive.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
String message = "Cannot copy entry " + entryName + " from " + source + " to " + file;
|
||||
@@ -272,7 +238,7 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
|
||||
ZipOutputStream out = mapArchiveStreams.get(file);
|
||||
out.putNextEntry(new ZipEntry(entryName));
|
||||
if (content != null) {
|
||||
out.write(content.getBytes("UTF-8"));
|
||||
out.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
@@ -282,10 +248,7 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
|
||||
}
|
||||
|
||||
private boolean checkEntry(String entryName, String file) {
|
||||
Set<String> set = mapArchiveEntries.get(file);
|
||||
if (set == null) {
|
||||
mapArchiveEntries.put(file, set = new HashSet<String>());
|
||||
}
|
||||
Set<String> set = mapArchiveEntries.computeIfAbsent(file, k -> new HashSet<>());
|
||||
|
||||
boolean added = set.add(entryName);
|
||||
if (!added) {
|
||||
@@ -306,4 +269,4 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
|
||||
DecompilerContext.getLogger().writeMessage("Cannot close " + file, IFernflowerLogger.Severity.WARN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,8 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
import org.jetbrains.java.decompiler.util.TextUtil;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
@@ -33,14 +19,14 @@ public class PrintStreamLogger extends IFernflowerLogger {
|
||||
@Override
|
||||
public void writeMessage(String message, Severity severity) {
|
||||
if (accepts(severity)) {
|
||||
stream.println(severity.prefix + InterpreterUtil.getIndentString(indent) + message);
|
||||
stream.println(severity.prefix + TextUtil.getIndentString(indent) + message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeMessage(String message, Throwable t) {
|
||||
if (accepts(Severity.ERROR)) {
|
||||
writeMessage(message, Severity.ERROR);
|
||||
public void writeMessage(String message, Severity severity, Throwable t) {
|
||||
if (accepts(severity)) {
|
||||
writeMessage(message, severity);
|
||||
t.printStackTrace(stream);
|
||||
}
|
||||
}
|
||||
@@ -85,6 +71,7 @@ public class PrintStreamLogger extends IFernflowerLogger {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endMethod() {
|
||||
if (accepts(Severity.INFO)) {
|
||||
--indent;
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.extern;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.extern;
|
||||
|
||||
public abstract class IFernflowerLogger {
|
||||
@@ -39,7 +25,11 @@ public abstract class IFernflowerLogger {
|
||||
|
||||
public abstract void writeMessage(String message, Severity severity);
|
||||
|
||||
public abstract void writeMessage(String message, Throwable t);
|
||||
public abstract void writeMessage(String message, Severity severity, Throwable t);
|
||||
|
||||
public void writeMessage(String message, Throwable t) {
|
||||
writeMessage(message, Severity.ERROR, t);
|
||||
}
|
||||
|
||||
public void startReadingClass(String className) { }
|
||||
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.extern;
|
||||
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
@@ -31,6 +17,7 @@ public interface IFernflowerPreferences {
|
||||
String HIDE_DEFAULT_CONSTRUCTOR = "hdc";
|
||||
String DECOMPILE_GENERIC_SIGNATURES = "dgs";
|
||||
String NO_EXCEPTIONS_RETURN = "ner";
|
||||
String ENSURE_SYNCHRONIZED_MONITOR = "esm";
|
||||
String DECOMPILE_ENUM = "den";
|
||||
String REMOVE_GET_CLASS_NEW = "rgn";
|
||||
String LITERALS_AS_IS = "lit";
|
||||
@@ -39,12 +26,14 @@ public interface IFernflowerPreferences {
|
||||
String SYNTHETIC_NOT_SET = "nns";
|
||||
String UNDEFINED_PARAM_TYPE_OBJECT = "uto";
|
||||
String USE_DEBUG_VAR_NAMES = "udv";
|
||||
String USE_METHOD_PARAMETERS = "ump";
|
||||
String REMOVE_EMPTY_RANGES = "rer";
|
||||
String FINALLY_DEINLINE = "fdi";
|
||||
String IDEA_NOT_NULL_ANNOTATION = "inn";
|
||||
String LAMBDA_TO_ANONYMOUS_CLASS = "lac";
|
||||
String BYTECODE_SOURCE_MAPPING = "bsm";
|
||||
String USE_DEBUG_LINE_NUMBERS = "udl";
|
||||
String IGNORE_INVALID_BYTECODE = "iib";
|
||||
String VERIFY_ANONYMOUS_CLASSES = "vac";
|
||||
|
||||
String LOG_LEVEL = "log";
|
||||
String MAX_PROCESSING_METHOD = "mpm";
|
||||
@@ -58,40 +47,49 @@ public interface IFernflowerPreferences {
|
||||
String UNIT_TEST_MODE = "__unit_test_mode__";
|
||||
|
||||
String LINE_SEPARATOR_WIN = "\r\n";
|
||||
String LINE_SEPARATOR_LIN = "\n";
|
||||
String LINE_SEPARATOR_UNX = "\n";
|
||||
|
||||
Map<String, Object> DEFAULTS = Collections.unmodifiableMap(new HashMap<String, Object>() {{
|
||||
put(REMOVE_BRIDGE, "1");
|
||||
put(REMOVE_SYNTHETIC, "0");
|
||||
put(DECOMPILE_INNER, "1");
|
||||
put(DECOMPILE_CLASS_1_4, "1");
|
||||
put(DECOMPILE_ASSERTIONS, "1");
|
||||
put(HIDE_EMPTY_SUPER, "1");
|
||||
put(HIDE_DEFAULT_CONSTRUCTOR, "1");
|
||||
put(DECOMPILE_GENERIC_SIGNATURES, "0");
|
||||
put(NO_EXCEPTIONS_RETURN, "1");
|
||||
put(DECOMPILE_ENUM, "1");
|
||||
put(REMOVE_GET_CLASS_NEW, "1");
|
||||
put(LITERALS_AS_IS, "0");
|
||||
put(BOOLEAN_TRUE_ONE, "1");
|
||||
put(ASCII_STRING_CHARACTERS, "0");
|
||||
put(SYNTHETIC_NOT_SET, "1");
|
||||
put(UNDEFINED_PARAM_TYPE_OBJECT, "1");
|
||||
put(USE_DEBUG_VAR_NAMES, "1");
|
||||
put(REMOVE_EMPTY_RANGES, "1");
|
||||
put(FINALLY_DEINLINE, "1");
|
||||
put(IDEA_NOT_NULL_ANNOTATION, "1");
|
||||
put(LAMBDA_TO_ANONYMOUS_CLASS, "0");
|
||||
put(BYTECODE_SOURCE_MAPPING, "0");
|
||||
put(USE_DEBUG_LINE_NUMBERS, "0");
|
||||
Map<String, Object> DEFAULTS = getDefaults();
|
||||
|
||||
put(LOG_LEVEL, IFernflowerLogger.Severity.INFO.name());
|
||||
put(MAX_PROCESSING_METHOD, "0");
|
||||
put(RENAME_ENTITIES, "0");
|
||||
put(NEW_LINE_SEPARATOR, (InterpreterUtil.IS_WINDOWS ? "0" : "1"));
|
||||
put(INDENT_STRING, " ");
|
||||
put(BANNER, "");
|
||||
put(UNIT_TEST_MODE, "0");
|
||||
put(DUMP_ORIGINAL_LINES, "0");
|
||||
}});
|
||||
}
|
||||
static Map<String, Object> getDefaults() {
|
||||
Map<String, Object> defaults = new HashMap<>();
|
||||
|
||||
defaults.put(REMOVE_BRIDGE, "1");
|
||||
defaults.put(REMOVE_SYNTHETIC, "0");
|
||||
defaults.put(DECOMPILE_INNER, "1");
|
||||
defaults.put(DECOMPILE_CLASS_1_4, "1");
|
||||
defaults.put(DECOMPILE_ASSERTIONS, "1");
|
||||
defaults.put(HIDE_EMPTY_SUPER, "1");
|
||||
defaults.put(HIDE_DEFAULT_CONSTRUCTOR, "1");
|
||||
defaults.put(DECOMPILE_GENERIC_SIGNATURES, "0");
|
||||
defaults.put(NO_EXCEPTIONS_RETURN, "1");
|
||||
defaults.put(ENSURE_SYNCHRONIZED_MONITOR, "1");
|
||||
defaults.put(DECOMPILE_ENUM, "1");
|
||||
defaults.put(REMOVE_GET_CLASS_NEW, "1");
|
||||
defaults.put(LITERALS_AS_IS, "0");
|
||||
defaults.put(BOOLEAN_TRUE_ONE, "1");
|
||||
defaults.put(ASCII_STRING_CHARACTERS, "0");
|
||||
defaults.put(SYNTHETIC_NOT_SET, "0");
|
||||
defaults.put(UNDEFINED_PARAM_TYPE_OBJECT, "1");
|
||||
defaults.put(USE_DEBUG_VAR_NAMES, "1");
|
||||
defaults.put(USE_METHOD_PARAMETERS, "1");
|
||||
defaults.put(REMOVE_EMPTY_RANGES, "1");
|
||||
defaults.put(FINALLY_DEINLINE, "1");
|
||||
defaults.put(IDEA_NOT_NULL_ANNOTATION, "1");
|
||||
defaults.put(LAMBDA_TO_ANONYMOUS_CLASS, "0");
|
||||
defaults.put(BYTECODE_SOURCE_MAPPING, "0");
|
||||
defaults.put(IGNORE_INVALID_BYTECODE, "0");
|
||||
defaults.put(VERIFY_ANONYMOUS_CLASSES, "0");
|
||||
|
||||
defaults.put(LOG_LEVEL, IFernflowerLogger.Severity.INFO.name());
|
||||
defaults.put(MAX_PROCESSING_METHOD, "0");
|
||||
defaults.put(RENAME_ENTITIES, "0");
|
||||
defaults.put(NEW_LINE_SEPARATOR, (InterpreterUtil.IS_WINDOWS ? "0" : "1"));
|
||||
defaults.put(INDENT_STRING, " ");
|
||||
defaults.put(BANNER, "");
|
||||
defaults.put(UNIT_TEST_MODE, "0");
|
||||
defaults.put(DUMP_ORIGINAL_LINES, "0");
|
||||
|
||||
return Collections.unmodifiableMap(defaults);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.extern;
|
||||
|
||||
public interface IIdentifierRenamer {
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.extern;
|
||||
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.rels;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
@@ -22,62 +8,49 @@ import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
|
||||
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
|
||||
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
|
||||
import org.jetbrains.java.decompiler.struct.StructClass;
|
||||
import org.jetbrains.java.decompiler.struct.StructField;
|
||||
import org.jetbrains.java.decompiler.struct.StructMethod;
|
||||
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
|
||||
import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute;
|
||||
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
import org.jetbrains.java.decompiler.util.VBStyleCollection;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class ClassWrapper {
|
||||
|
||||
private final StructClass classStruct;
|
||||
private final Set<String> hiddenMembers = new HashSet<String>();
|
||||
private final VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<Exprent, String>();
|
||||
private final VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<Exprent, String>();
|
||||
private final VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<MethodWrapper, String>();
|
||||
private final Set<String> hiddenMembers = new HashSet<>();
|
||||
private final VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<>();
|
||||
private final VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<>();
|
||||
private final VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<>();
|
||||
|
||||
public ClassWrapper(StructClass classStruct) {
|
||||
this.classStruct = classStruct;
|
||||
}
|
||||
|
||||
public void init() throws IOException {
|
||||
public void init() {
|
||||
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct);
|
||||
DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_WRAPPER, this);
|
||||
DecompilerContext.getLogger().startClass(classStruct.qualifiedName);
|
||||
|
||||
// collect field names
|
||||
Set<String> setFieldNames = new HashSet<String>();
|
||||
for (StructField fd : classStruct.getFields()) {
|
||||
setFieldNames.add(fd.getName());
|
||||
}
|
||||
|
||||
int maxSec = Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString());
|
||||
boolean testMode = DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE);
|
||||
|
||||
for (StructMethod mt : classStruct.getMethods()) {
|
||||
DecompilerContext.getLogger().startMethod(mt.getName() + " " + mt.getDescriptor());
|
||||
|
||||
VarNamesCollector vc = new VarNamesCollector();
|
||||
DecompilerContext.setVarNamesCollector(vc);
|
||||
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
|
||||
VarProcessor varProc = new VarProcessor(mt, md);
|
||||
DecompilerContext.startMethod(varProc);
|
||||
|
||||
CounterContainer counter = new CounterContainer();
|
||||
DecompilerContext.setCounterContainer(counter);
|
||||
|
||||
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD, mt);
|
||||
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR, MethodDescriptor.parseDescriptor(mt.getDescriptor()));
|
||||
|
||||
VarProcessor varProc = new VarProcessor();
|
||||
DecompilerContext.setProperty(DecompilerContext.CURRENT_VAR_PROCESSOR, varProc);
|
||||
VarNamesCollector vc = varProc.getVarNamesCollector();
|
||||
CounterContainer counter = DecompilerContext.getCounterContainer();
|
||||
|
||||
RootStatement root = null;
|
||||
|
||||
@@ -86,13 +59,13 @@ public class ClassWrapper {
|
||||
try {
|
||||
if (mt.containsCode()) {
|
||||
if (maxSec == 0 || testMode) {
|
||||
root = MethodProcessorRunnable.codeToJava(mt, varProc);
|
||||
root = MethodProcessorRunnable.codeToJava(mt, md, varProc);
|
||||
}
|
||||
else {
|
||||
MethodProcessorRunnable mtProc = new MethodProcessorRunnable(mt, varProc, DecompilerContext.getCurrentContext());
|
||||
MethodProcessorRunnable mtProc = new MethodProcessorRunnable(mt, md, varProc, DecompilerContext.getCurrentContext());
|
||||
|
||||
Thread mtThread = new Thread(mtProc, "Java decompiler");
|
||||
long stopAt = System.currentTimeMillis() + maxSec * 1000;
|
||||
long stopAt = System.currentTimeMillis() + maxSec * 1000L;
|
||||
|
||||
mtThread.start();
|
||||
|
||||
@@ -123,7 +96,6 @@ public class ClassWrapper {
|
||||
}
|
||||
else {
|
||||
boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC);
|
||||
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
|
||||
|
||||
int paramCount = 0;
|
||||
if (thisVar) {
|
||||
@@ -150,8 +122,9 @@ public class ClassWrapper {
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled.", ex);
|
||||
catch (Throwable t) {
|
||||
String message = "Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled.";
|
||||
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t);
|
||||
isError = true;
|
||||
}
|
||||
|
||||
@@ -160,16 +133,35 @@ public class ClassWrapper {
|
||||
|
||||
methods.addWithKey(methodWrapper, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));
|
||||
|
||||
// rename vars so that no one has the same name as a field
|
||||
varProc.refreshVarNames(new VarNamesCollector(setFieldNames));
|
||||
if (!isError) {
|
||||
// rename vars so that no one has the same name as a field
|
||||
VarNamesCollector namesCollector = new VarNamesCollector();
|
||||
classStruct.getFields().forEach(f -> namesCollector.addName(f.getName()));
|
||||
varProc.refreshVarNames(namesCollector);
|
||||
|
||||
// if debug information present and should be used
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) {
|
||||
StructLocalVariableTableAttribute attr = (StructLocalVariableTableAttribute)mt.getAttributes().getWithKey(
|
||||
StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE);
|
||||
// if debug information present and should be used
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) {
|
||||
StructLocalVariableTableAttribute attr = mt.getLocalVariableAttr();
|
||||
if (attr != null) {
|
||||
// only param names here
|
||||
varProc.setDebugVarNames(attr.getMapParamNames());
|
||||
|
||||
if (attr != null) {
|
||||
varProc.setDebugVarNames(attr.getMapVarNames());
|
||||
// the rest is here
|
||||
methodWrapper.getOrBuildGraph().iterateExprents(exprent -> {
|
||||
List<Exprent> lst = exprent.getAllExprents(true);
|
||||
lst.add(exprent);
|
||||
lst.stream()
|
||||
.filter(e -> e.type == Exprent.EXPRENT_VAR)
|
||||
.forEach(e -> {
|
||||
VarExprent varExprent = (VarExprent)e;
|
||||
String name = varExprent.getDebugName(mt);
|
||||
if (name != null) {
|
||||
varProc.setVarName(varExprent.getVarVersionPair(), name);
|
||||
}
|
||||
});
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,4 +199,9 @@ public class ClassWrapper {
|
||||
public VBStyleCollection<Exprent, String> getDynamicFieldInitializers() {
|
||||
return dynamicFieldInitializers;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return classStruct.qualifiedName;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.rels;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
@@ -44,21 +30,17 @@ public class LambdaProcessor {
|
||||
processClass(child);
|
||||
}
|
||||
|
||||
hasLambda(node);
|
||||
}
|
||||
|
||||
public boolean hasLambda(ClassNode node) throws IOException {
|
||||
ClassesProcessor clProcessor = DecompilerContext.getClassProcessor();
|
||||
StructClass cl = node.classStruct;
|
||||
|
||||
if (cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lambda beginning with Java 8
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
StructBootstrapMethodsAttribute bootstrap =
|
||||
(StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
|
||||
cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
|
||||
if (bootstrap == null || bootstrap.getMethodsNumber() == 0) {
|
||||
return false; // no bootstrap constants in pool
|
||||
return; // no bootstrap constants in pool
|
||||
}
|
||||
|
||||
BitSet lambda_methods = new BitSet();
|
||||
@@ -75,10 +57,10 @@ public class LambdaProcessor {
|
||||
}
|
||||
|
||||
if (lambda_methods.isEmpty()) {
|
||||
return false; // no lambda bootstrap constant found
|
||||
return; // no lambda bootstrap constant found
|
||||
}
|
||||
|
||||
Map<String, String> mapMethodsLambda = new HashMap<String, String>();
|
||||
Map<String, String> mapMethodsLambda = new HashMap<>();
|
||||
|
||||
// iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes.
|
||||
for (StructMethod mt : cl.getMethods()) {
|
||||
@@ -92,7 +74,7 @@ public class LambdaProcessor {
|
||||
Instruction instr = seq.getInstr(i);
|
||||
|
||||
if (instr.opcode == CodeConstants.opc_invokedynamic) {
|
||||
LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.getOperand(0));
|
||||
LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.operand(0));
|
||||
|
||||
if (lambda_methods.get(invoke_dynamic.index1)) { // lambda invocation found
|
||||
|
||||
@@ -115,7 +97,9 @@ public class LambdaProcessor {
|
||||
node_lambda.parent = node;
|
||||
|
||||
clProcessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda);
|
||||
mapMethodsLambda.put(node_lambda.lambdaInformation.content_method_key, node_lambda.simpleName);
|
||||
if (!node_lambda.lambdaInformation.is_method_reference) {
|
||||
mapMethodsLambda.put(node_lambda.lambdaInformation.content_method_key, node_lambda.simpleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,7 +122,5 @@ public class LambdaProcessor {
|
||||
}
|
||||
|
||||
// FIXME: mixed hierarchy?
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.rels;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
@@ -29,14 +15,15 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
|
||||
import org.jetbrains.java.decompiler.struct.StructClass;
|
||||
import org.jetbrains.java.decompiler.struct.StructMethod;
|
||||
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class MethodProcessorRunnable implements Runnable {
|
||||
|
||||
public final Object lock = new Object();
|
||||
|
||||
private final StructMethod method;
|
||||
private final MethodDescriptor methodDescriptor;
|
||||
private final VarProcessor varProc;
|
||||
private final DecompilerContext parentContext;
|
||||
|
||||
@@ -44,27 +31,27 @@ public class MethodProcessorRunnable implements Runnable {
|
||||
private volatile Throwable error;
|
||||
private volatile boolean finished = false;
|
||||
|
||||
public MethodProcessorRunnable(StructMethod method, VarProcessor varProc, DecompilerContext parentContext) {
|
||||
public MethodProcessorRunnable(StructMethod method,
|
||||
MethodDescriptor methodDescriptor,
|
||||
VarProcessor varProc,
|
||||
DecompilerContext parentContext) {
|
||||
this.method = method;
|
||||
this.methodDescriptor = methodDescriptor;
|
||||
this.varProc = varProc;
|
||||
this.parentContext = parentContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
DecompilerContext.setCurrentContext(parentContext);
|
||||
|
||||
error = null;
|
||||
root = null;
|
||||
|
||||
try {
|
||||
root = codeToJava(method, varProc);
|
||||
DecompilerContext.setCurrentContext(parentContext);
|
||||
root = codeToJava(method, methodDescriptor, varProc);
|
||||
}
|
||||
catch (ThreadDeath ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
error = ex;
|
||||
catch (Throwable t) {
|
||||
error = t;
|
||||
}
|
||||
finally {
|
||||
DecompilerContext.setCurrentContext(null);
|
||||
@@ -76,7 +63,7 @@ public class MethodProcessorRunnable implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
public static RootStatement codeToJava(StructMethod mt, VarProcessor varProc) throws IOException {
|
||||
public static RootStatement codeToJava(StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException {
|
||||
StructClass cl = mt.getClassStruct();
|
||||
|
||||
boolean isInitializer = CodeConstants.CLINIT_NAME.equals(mt.getName()); // for now static initializer only
|
||||
@@ -101,6 +88,11 @@ public class MethodProcessorRunnable implements Runnable {
|
||||
ExceptionDeobfuscator.removeEmptyRanges(graph);
|
||||
}
|
||||
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.ENSURE_SYNCHRONIZED_MONITOR)) {
|
||||
// special case: search for 'synchronized' ranges w/o monitorexit instruction (as generated by Kotlin and Scala)
|
||||
DeadCodeHelper.extendSynchronizedRangeToMonitorexit(graph);
|
||||
}
|
||||
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) {
|
||||
// special case: single return instruction outside of a protected range
|
||||
DeadCodeHelper.incorporateValueReturns(graph);
|
||||
@@ -115,11 +107,15 @@ public class MethodProcessorRunnable implements Runnable {
|
||||
|
||||
if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) {
|
||||
DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.Severity.WARN);
|
||||
if (!ExceptionDeobfuscator.handleMultipleEntryExceptionRanges(graph)) {
|
||||
DecompilerContext.getLogger().writeMessage("Found multiple entry exception ranges which could not be splitted", IFernflowerLogger.Severity.WARN);
|
||||
}
|
||||
ExceptionDeobfuscator.insertDummyExceptionHandlerBlocks(graph, cl.getBytecodeVersion());
|
||||
}
|
||||
|
||||
RootStatement root = DomHelper.parseGraph(graph);
|
||||
|
||||
FinallyProcessor fProc = new FinallyProcessor(varProc);
|
||||
FinallyProcessor fProc = new FinallyProcessor(md, varProc);
|
||||
while (fProc.iterateGraph(mt, root, graph)) {
|
||||
root = DomHelper.parseGraph(graph);
|
||||
}
|
||||
@@ -134,44 +130,31 @@ public class MethodProcessorRunnable implements Runnable {
|
||||
|
||||
ClearStructHelper.clearStatements(root);
|
||||
|
||||
ExprProcessor proc = new ExprProcessor();
|
||||
ExprProcessor proc = new ExprProcessor(md, varProc);
|
||||
proc.processStatement(root, cl);
|
||||
|
||||
SequenceHelper.condenseSequences(root);
|
||||
|
||||
while (true) {
|
||||
StackVarsProcessor stackProc = new StackVarsProcessor();
|
||||
|
||||
StackVarsProcessor stackProc = new StackVarsProcessor();
|
||||
|
||||
do {
|
||||
stackProc.simplifyStackVars(root, mt, cl);
|
||||
|
||||
varProc.setVarVersions(root);
|
||||
|
||||
if (!new PPandMMHelper().findPPandMM(root)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (new PPandMMHelper().findPPandMM(root));
|
||||
|
||||
while (true) {
|
||||
LabelHelper.cleanUpEdges(root);
|
||||
|
||||
while (true) {
|
||||
do {
|
||||
MergeHelper.enhanceLoops(root);
|
||||
|
||||
if (LoopExtractHelper.extractLoops(root)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IfHelper.mergeAllIfs(root)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (LoopExtractHelper.extractLoops(root) || IfHelper.mergeAllIfs(root));
|
||||
|
||||
if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) {
|
||||
if (IdeaNotNullHelper.removeHardcodedChecks(root, mt)) {
|
||||
SequenceHelper.condenseSequences(root);
|
||||
|
||||
StackVarsProcessor stackProc = new StackVarsProcessor();
|
||||
stackProc.simplifyStackVars(root, mt, cl);
|
||||
|
||||
varProc.setVarVersions(root);
|
||||
}
|
||||
}
|
||||
@@ -188,14 +171,14 @@ public class MethodProcessorRunnable implements Runnable {
|
||||
}
|
||||
|
||||
// FIXME: !!
|
||||
// if(!EliminateLoopsHelper.eliminateLoops(root)) {
|
||||
// break;
|
||||
// }
|
||||
//if(!EliminateLoopsHelper.eliminateLoops(root)) {
|
||||
// break;
|
||||
//}
|
||||
}
|
||||
|
||||
ExitHelper.removeRedundantReturns(root);
|
||||
|
||||
SecondaryFunctionsHelper.identifySecondaryFunctions(root);
|
||||
SecondaryFunctionsHelper.identifySecondaryFunctions(root, varProc);
|
||||
|
||||
varProc.setVarDefinitions(root);
|
||||
|
||||
@@ -217,4 +200,4 @@ public class MethodProcessorRunnable implements Runnable {
|
||||
public boolean isFinished() {
|
||||
return finished;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.rels;
|
||||
|
||||
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
|
||||
@@ -25,26 +11,19 @@ import org.jetbrains.java.decompiler.struct.StructMethod;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class MethodWrapper {
|
||||
|
||||
public final RootStatement root;
|
||||
|
||||
public final VarProcessor varproc;
|
||||
|
||||
public final StructMethod methodStruct;
|
||||
|
||||
public final CounterContainer counter;
|
||||
public final Set<String> setOuterVarNames = new HashSet<>();
|
||||
|
||||
public DirectGraph graph;
|
||||
|
||||
public List<VarVersionPair> signatureFields;
|
||||
|
||||
public List<VarVersionPair> synthParameters;
|
||||
public boolean decompiledWithErrors;
|
||||
|
||||
public final HashSet<String> setOuterVarNames = new HashSet<String>();
|
||||
|
||||
public MethodWrapper(RootStatement root, VarProcessor varproc, StructMethod methodStruct, CounterContainer counter) {
|
||||
this.root = root;
|
||||
this.varproc = varproc;
|
||||
@@ -54,9 +33,13 @@ public class MethodWrapper {
|
||||
|
||||
public DirectGraph getOrBuildGraph() {
|
||||
if (graph == null && root != null) {
|
||||
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
|
||||
graph = flatthelper.buildDirectGraph(root);
|
||||
graph = new FlattenStatementsHelper().buildDirectGraph(root);
|
||||
}
|
||||
return graph;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return methodStruct.getName();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.main.rels;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
@@ -29,20 +15,14 @@ import org.jetbrains.java.decompiler.struct.StructMethod;
|
||||
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public class NestedMemberAccess {
|
||||
|
||||
private static final int METHOD_ACCESS_NORMAL = 1;
|
||||
private static final int METHOD_ACCESS_FIELD_GET = 2;
|
||||
private static final int METHOD_ACCESS_FIELD_SET = 3;
|
||||
private static final int METHOD_ACCESS_METHOD = 4;
|
||||
private enum MethodAccess {NORMAL, FIELD_GET, FIELD_SET, METHOD, FUNCTION}
|
||||
|
||||
private boolean noSynthFlag;
|
||||
private final Map<MethodWrapper, Integer> mapMethodType = new HashMap<MethodWrapper, Integer>();
|
||||
private final Map<MethodWrapper, MethodAccess> mapMethodType = new HashMap<>();
|
||||
|
||||
|
||||
public void propagateMemberAccess(ClassNode root) {
|
||||
@@ -73,7 +53,7 @@ public class NestedMemberAccess {
|
||||
}
|
||||
|
||||
private void computeMethodType(ClassNode node, MethodWrapper method) {
|
||||
int type = METHOD_ACCESS_NORMAL;
|
||||
MethodAccess type = MethodAccess.NORMAL;
|
||||
|
||||
if (method.root != null) {
|
||||
DirectGraph graph = method.getOrBuildGraph();
|
||||
@@ -104,7 +84,7 @@ public class NestedMemberAccess {
|
||||
if (fexpr.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
|
||||
if (fexpr.isStatic() ||
|
||||
(fexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpr.getInstance()).getIndex() == 0)) {
|
||||
type = METHOD_ACCESS_FIELD_GET;
|
||||
type = MethodAccess.FIELD_GET;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,13 +93,23 @@ public class NestedMemberAccess {
|
||||
if (parcount == 1) {
|
||||
// this or final variable
|
||||
if (((VarExprent)exprCore).getIndex() != 0) {
|
||||
type = METHOD_ACCESS_FIELD_GET;
|
||||
type = MethodAccess.FIELD_GET;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case Exprent.EXPRENT_FUNCTION:
|
||||
// for now detect only increment/decrement
|
||||
FunctionExprent functionExprent = (FunctionExprent)exprCore;
|
||||
if (functionExprent.getFuncType() >= FunctionExprent.FUNCTION_IMM &&
|
||||
functionExprent.getFuncType() <= FunctionExprent.FUNCTION_PPI) {
|
||||
if (functionExprent.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) {
|
||||
type = MethodAccess.FUNCTION;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Exprent.EXPRENT_INVOCATION:
|
||||
type = METHOD_ACCESS_METHOD;
|
||||
type = MethodAccess.METHOD;
|
||||
break;
|
||||
case Exprent.EXPRENT_ASSIGNMENT:
|
||||
AssignmentExprent asexpr = (AssignmentExprent)exprCore;
|
||||
@@ -131,7 +121,7 @@ public class NestedMemberAccess {
|
||||
if (fexpras.isStatic() ||
|
||||
(fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {
|
||||
if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
|
||||
type = METHOD_ACCESS_FIELD_SET;
|
||||
type = MethodAccess.FIELD_SET;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,30 +129,31 @@ public class NestedMemberAccess {
|
||||
}
|
||||
}
|
||||
|
||||
if (type == MethodAccess.METHOD) { // FIXME: check for private flag of the method
|
||||
|
||||
if (type == METHOD_ACCESS_METHOD) { // FIXME: check for private flag of the method
|
||||
|
||||
type = METHOD_ACCESS_NORMAL;
|
||||
type = MethodAccess.NORMAL;
|
||||
|
||||
InvocationExprent invexpr = (InvocationExprent)exprCore;
|
||||
|
||||
if ((invexpr.isStatic() && invexpr.getLstParameters().size() == parcount) ||
|
||||
(!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR
|
||||
boolean isStatic = invexpr.isStatic();
|
||||
if ((isStatic && invexpr.getLstParameters().size() == parcount) ||
|
||||
(!isStatic && invexpr.getInstance().type == Exprent.EXPRENT_VAR
|
||||
&& ((VarExprent)invexpr.getInstance()).getIndex() == 0 && invexpr.getLstParameters().size() == parcount - 1)) {
|
||||
|
||||
boolean equalpars = true;
|
||||
|
||||
int index = isStatic ? 0 : 1;
|
||||
for (int i = 0; i < invexpr.getLstParameters().size(); i++) {
|
||||
Exprent parexpr = invexpr.getLstParameters().get(i);
|
||||
if (parexpr.type != Exprent.EXPRENT_VAR ||
|
||||
((VarExprent)parexpr).getIndex() != i + (invexpr.isStatic() ? 0 : 1)) {
|
||||
if (parexpr.type != Exprent.EXPRENT_VAR || ((VarExprent)parexpr).getIndex() != index) {
|
||||
equalpars = false;
|
||||
break;
|
||||
}
|
||||
index += mtdesc.params[i + (isStatic ? 0 : 1)].stackSize;
|
||||
}
|
||||
|
||||
if (equalpars) {
|
||||
type = METHOD_ACCESS_METHOD;
|
||||
type = MethodAccess.METHOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,7 +182,7 @@ public class NestedMemberAccess {
|
||||
if (exexpr.getExitType() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {
|
||||
if (exexpr.getValue().type == Exprent.EXPRENT_VAR &&
|
||||
((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
|
||||
type = METHOD_ACCESS_FIELD_SET;
|
||||
type = MethodAccess.FIELD_SET;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,7 +196,7 @@ public class NestedMemberAccess {
|
||||
}
|
||||
}
|
||||
|
||||
if (type != METHOD_ACCESS_NORMAL) {
|
||||
if (type != MethodAccess.NORMAL) {
|
||||
mapMethodType.put(method, type);
|
||||
}
|
||||
else {
|
||||
@@ -228,8 +219,8 @@ public class NestedMemberAccess {
|
||||
|
||||
DirectGraph graph = meth.getOrBuildGraph();
|
||||
|
||||
HashSet<DirectNode> setVisited = new HashSet<DirectNode>();
|
||||
LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
|
||||
HashSet<DirectNode> setVisited = new HashSet<>();
|
||||
LinkedList<DirectNode> stack = new LinkedList<>();
|
||||
stack.add(graph.first);
|
||||
|
||||
while (!stack.isEmpty()) { // TODO: replace with interface iterator?
|
||||
@@ -256,9 +247,7 @@ public class NestedMemberAccess {
|
||||
}
|
||||
}
|
||||
|
||||
for (DirectNode ndx : nd.succs) {
|
||||
stack.add(ndx);
|
||||
}
|
||||
stack.addAll(nd.succs);
|
||||
}
|
||||
|
||||
if (replaced) {
|
||||
@@ -323,7 +312,6 @@ public class NestedMemberAccess {
|
||||
}
|
||||
|
||||
private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, InvocationExprent invexpr) {
|
||||
|
||||
ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(invexpr.getClassname());
|
||||
|
||||
MethodWrapper methsource = null;
|
||||
@@ -343,10 +331,10 @@ public class NestedMemberAccess {
|
||||
return null;
|
||||
}
|
||||
|
||||
int type = mapMethodType.get(methsource);
|
||||
MethodAccess type = mapMethodType.get(methsource);
|
||||
|
||||
// // FIXME: impossible case. METHOD_ACCESS_NORMAL is not saved in the map
|
||||
// if(type == METHOD_ACCESS_NORMAL) {
|
||||
// // FIXME: impossible case. MethodAccess.NORMAL is not saved in the map
|
||||
// if(type == MethodAccess.NORMAL) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
@@ -360,7 +348,7 @@ public class NestedMemberAccess {
|
||||
Exprent retexprent = null;
|
||||
|
||||
switch (type) {
|
||||
case METHOD_ACCESS_FIELD_GET:
|
||||
case FIELD_GET:
|
||||
ExitExprent exsource = (ExitExprent)source;
|
||||
if (exsource.getValue().type == Exprent.EXPRENT_VAR) { // qualified this
|
||||
VarExprent var = (VarExprent)exsource.getValue();
|
||||
@@ -388,7 +376,7 @@ public class NestedMemberAccess {
|
||||
retexprent = ret;
|
||||
}
|
||||
break;
|
||||
case METHOD_ACCESS_FIELD_SET:
|
||||
case FIELD_SET:
|
||||
AssignmentExprent ret;
|
||||
if (source.type == Exprent.EXPRENT_EXIT) {
|
||||
ExitExprent extex = (ExitExprent)source;
|
||||
@@ -406,9 +394,17 @@ public class NestedMemberAccess {
|
||||
ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(1));
|
||||
fexpr.replaceExprent(fexpr.getInstance(), invexpr.getLstParameters().get(0));
|
||||
}
|
||||
|
||||
// do not use copied bytecodes
|
||||
ret.getLeft().bytecode = null;
|
||||
ret.getRight().bytecode = null;
|
||||
|
||||
retexprent = ret;
|
||||
break;
|
||||
case METHOD_ACCESS_METHOD:
|
||||
case FUNCTION:
|
||||
retexprent = replaceFunction(invexpr, source);
|
||||
break;
|
||||
case METHOD:
|
||||
if (source.type == Exprent.EXPRENT_EXIT) {
|
||||
source = ((ExitExprent)source).getValue();
|
||||
}
|
||||
@@ -430,6 +426,10 @@ public class NestedMemberAccess {
|
||||
|
||||
|
||||
if (retexprent != null) {
|
||||
// preserve original bytecodes
|
||||
retexprent.bytecode = null;
|
||||
retexprent.addBytecodeOffsets(invexpr.bytecode);
|
||||
|
||||
// hide synthetic access method
|
||||
boolean hide = true;
|
||||
|
||||
@@ -446,4 +446,25 @@ public class NestedMemberAccess {
|
||||
|
||||
return retexprent;
|
||||
}
|
||||
|
||||
private static Exprent replaceFunction(final InvocationExprent invexpr, final Exprent source) {
|
||||
FunctionExprent functionExprent = (FunctionExprent)((ExitExprent)source).getValue().copy();
|
||||
|
||||
List<Exprent> lstParameters = invexpr.getLstParameters();
|
||||
|
||||
FieldExprent fieldExprent = (FieldExprent)functionExprent.getLstOperands().get(0);
|
||||
if (fieldExprent.isStatic()) {
|
||||
if (!lstParameters.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return functionExprent;
|
||||
}
|
||||
|
||||
if (lstParameters.size() != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
fieldExprent.replaceExprent(fieldExprent.getInstance(), lstParameters.get(0));
|
||||
return functionExprent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,10 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.modules.code;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
import org.jetbrains.java.decompiler.code.InstructionSequence;
|
||||
import org.jetbrains.java.decompiler.code.SimpleInstructionSequence;
|
||||
import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
|
||||
import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
|
||||
import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;
|
||||
@@ -30,8 +17,8 @@ public class DeadCodeHelper {
|
||||
|
||||
public static void removeDeadBlocks(ControlFlowGraph graph) {
|
||||
|
||||
LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>();
|
||||
HashSet<BasicBlock> setStacked = new HashSet<BasicBlock>();
|
||||
LinkedList<BasicBlock> stack = new LinkedList<>();
|
||||
HashSet<BasicBlock> setStacked = new HashSet<>();
|
||||
|
||||
stack.add(graph.getFirst());
|
||||
setStacked.add(graph.getFirst());
|
||||
@@ -39,7 +26,7 @@ public class DeadCodeHelper {
|
||||
while (!stack.isEmpty()) {
|
||||
BasicBlock block = stack.removeFirst();
|
||||
|
||||
List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs());
|
||||
List<BasicBlock> lstSuccs = new ArrayList<>(block.getSuccs());
|
||||
lstSuccs.addAll(block.getSuccExceptions());
|
||||
|
||||
for (BasicBlock succ : lstSuccs) {
|
||||
@@ -50,7 +37,7 @@ public class DeadCodeHelper {
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<BasicBlock> setAllBlocks = new HashSet<BasicBlock>(graph.getBlocks());
|
||||
HashSet<BasicBlock> setAllBlocks = new HashSet<>(graph.getBlocks());
|
||||
setAllBlocks.removeAll(setStacked);
|
||||
|
||||
for (BasicBlock block : setAllBlocks) {
|
||||
@@ -94,7 +81,7 @@ public class DeadCodeHelper {
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<BasicBlock> setExits = new HashSet<BasicBlock>(graph.getLast().getPreds());
|
||||
HashSet<BasicBlock> setExits = new HashSet<>(graph.getLast().getPreds());
|
||||
|
||||
if (block.getPredExceptions().isEmpty() &&
|
||||
(!setExits.contains(block) || block.getPreds().size() == 1)) {
|
||||
@@ -109,15 +96,15 @@ public class DeadCodeHelper {
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<BasicBlock> setPreds = new HashSet<BasicBlock>(block.getPreds());
|
||||
HashSet<BasicBlock> setSuccs = new HashSet<BasicBlock>(block.getSuccs());
|
||||
HashSet<BasicBlock> setPreds = new HashSet<>(block.getPreds());
|
||||
HashSet<BasicBlock> setSuccs = new HashSet<>(block.getSuccs());
|
||||
|
||||
// collect common exception ranges of predecessors and successors
|
||||
HashSet<BasicBlock> setCommonExceptionHandlers = null;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (BasicBlock pred : i == 0 ? setPreds : setSuccs) {
|
||||
if (setCommonExceptionHandlers == null) {
|
||||
setCommonExceptionHandlers = new HashSet<BasicBlock>(pred.getSuccExceptions());
|
||||
setCommonExceptionHandlers = new HashSet<>(pred.getSuccExceptions());
|
||||
}
|
||||
else {
|
||||
setCommonExceptionHandlers.retainAll(pred.getSuccExceptions());
|
||||
@@ -159,7 +146,7 @@ public class DeadCodeHelper {
|
||||
BasicBlock pred = block.getPreds().get(0);
|
||||
pred.removeSuccessor(block);
|
||||
|
||||
List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs());
|
||||
List<BasicBlock> lstSuccs = new ArrayList<>(block.getSuccs());
|
||||
for (BasicBlock succ : lstSuccs) {
|
||||
block.removeSuccessor(succ);
|
||||
pred.addSuccessor(succ);
|
||||
@@ -205,13 +192,13 @@ public class DeadCodeHelper {
|
||||
|
||||
public static boolean isDominator(ControlFlowGraph graph, BasicBlock block, BasicBlock dom) {
|
||||
|
||||
HashSet<BasicBlock> marked = new HashSet<BasicBlock>();
|
||||
HashSet<BasicBlock> marked = new HashSet<>();
|
||||
|
||||
if (block == dom) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LinkedList<BasicBlock> lstNodes = new LinkedList<BasicBlock>();
|
||||
LinkedList<BasicBlock> lstNodes = new LinkedList<>();
|
||||
lstNodes.add(block);
|
||||
|
||||
while (!lstNodes.isEmpty()) {
|
||||
@@ -230,14 +217,14 @@ public class DeadCodeHelper {
|
||||
|
||||
for (int i = 0; i < node.getPreds().size(); i++) {
|
||||
BasicBlock pred = node.getPreds().get(i);
|
||||
if (!marked.contains(pred) && pred != dom) {
|
||||
if (pred != dom && !marked.contains(pred)) {
|
||||
lstNodes.add(pred);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < node.getPredExceptions().size(); i++) {
|
||||
BasicBlock pred = node.getPredExceptions().get(i);
|
||||
if (!marked.contains(pred) && pred != dom) {
|
||||
if (pred != dom && !marked.contains(pred)) {
|
||||
lstNodes.add(pred);
|
||||
}
|
||||
}
|
||||
@@ -262,12 +249,158 @@ public class DeadCodeHelper {
|
||||
public static void connectDummyExitBlock(ControlFlowGraph graph) {
|
||||
|
||||
BasicBlock exit = graph.getLast();
|
||||
for (BasicBlock block : new HashSet<BasicBlock>(exit.getPreds())) {
|
||||
for (BasicBlock block : new HashSet<>(exit.getPreds())) {
|
||||
exit.removePredecessor(block);
|
||||
block.addSuccessor(exit);
|
||||
}
|
||||
}
|
||||
|
||||
public static void extendSynchronizedRangeToMonitorexit(ControlFlowGraph graph) {
|
||||
|
||||
while(true) {
|
||||
|
||||
boolean range_extended = false;
|
||||
|
||||
for (ExceptionRangeCFG range : graph.getExceptions()) {
|
||||
|
||||
Set<BasicBlock> setPreds = new HashSet<>();
|
||||
for (BasicBlock block : range.getProtectedRange()) {
|
||||
setPreds.addAll(block.getPreds());
|
||||
}
|
||||
setPreds.removeAll(range.getProtectedRange());
|
||||
|
||||
if(setPreds.size() != 1) {
|
||||
continue; // multiple predecessors, obfuscated range
|
||||
}
|
||||
|
||||
BasicBlock predBlock = setPreds.iterator().next();
|
||||
InstructionSequence predSeq = predBlock.getSeq();
|
||||
if(predSeq.isEmpty() || predSeq.getLastInstr().opcode != CodeConstants.opc_monitorenter) {
|
||||
continue; // not a synchronized range
|
||||
}
|
||||
|
||||
boolean monitorexit_in_range = false;
|
||||
Set<BasicBlock> setProtectedBlocks = new HashSet<>();
|
||||
setProtectedBlocks.addAll(range.getProtectedRange());
|
||||
setProtectedBlocks.add(range.getHandler());
|
||||
|
||||
for (BasicBlock block : setProtectedBlocks) {
|
||||
InstructionSequence blockSeq = block.getSeq();
|
||||
for (int i = 0; i < blockSeq.length(); i++) {
|
||||
if (blockSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
|
||||
monitorexit_in_range = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(monitorexit_in_range) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(monitorexit_in_range) {
|
||||
continue; // protected range already contains monitorexit
|
||||
}
|
||||
|
||||
Set<BasicBlock> setSuccs = new HashSet<>();
|
||||
for (BasicBlock block : range.getProtectedRange()) {
|
||||
setSuccs.addAll(block.getSuccs());
|
||||
}
|
||||
setSuccs.removeAll(range.getProtectedRange());
|
||||
|
||||
if(setSuccs.size() != 1) {
|
||||
continue; // non-unique successor
|
||||
}
|
||||
|
||||
BasicBlock succBlock = setSuccs.iterator().next();
|
||||
InstructionSequence succSeq = succBlock.getSeq();
|
||||
|
||||
int succ_monitorexit_index = -1;
|
||||
for (int i = 0; i < succSeq.length(); i++) {
|
||||
if (succSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
|
||||
succ_monitorexit_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(succ_monitorexit_index < 0) {
|
||||
continue; // monitorexit not found in the single successor block
|
||||
}
|
||||
|
||||
BasicBlock handlerBlock = range.getHandler();
|
||||
if(handlerBlock.getSuccs().size() != 1) {
|
||||
continue; // non-unique handler successor
|
||||
}
|
||||
BasicBlock succHandler = handlerBlock.getSuccs().get(0);
|
||||
InstructionSequence succHandlerSeq = succHandler.getSeq();
|
||||
if(succHandlerSeq.isEmpty() || succHandlerSeq.getLastInstr().opcode != CodeConstants.opc_athrow) {
|
||||
continue; // not a standard synchronized range
|
||||
}
|
||||
|
||||
int handler_monitorexit_index = -1;
|
||||
for (int i = 0; i < succHandlerSeq.length(); i++) {
|
||||
if (succHandlerSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
|
||||
handler_monitorexit_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(handler_monitorexit_index < 0) {
|
||||
continue; // monitorexit not found in the handler successor block
|
||||
}
|
||||
|
||||
// checks successful, prerequisites satisfied, now extend the range
|
||||
if(succ_monitorexit_index < succSeq.length() - 1) { // split block
|
||||
|
||||
SimpleInstructionSequence seq = new SimpleInstructionSequence();
|
||||
for(int counter = 0; counter < succ_monitorexit_index; counter++) {
|
||||
seq.addInstruction(succSeq.getInstr(0), -1);
|
||||
succSeq.removeInstruction(0);
|
||||
}
|
||||
|
||||
// build a separate block
|
||||
BasicBlock newblock = new BasicBlock(++graph.last_id);
|
||||
newblock.setSeq(seq);
|
||||
|
||||
// insert new block
|
||||
for (BasicBlock block : succBlock.getPreds()) {
|
||||
block.replaceSuccessor(succBlock, newblock);
|
||||
}
|
||||
|
||||
newblock.addSuccessor(succBlock);
|
||||
graph.getBlocks().addWithKey(newblock, newblock.id);
|
||||
|
||||
succBlock = newblock;
|
||||
}
|
||||
|
||||
// copy exception edges and extend protected ranges (successor block)
|
||||
BasicBlock rangeExitBlock = succBlock.getPreds().get(0);
|
||||
for (int j = 0; j < rangeExitBlock.getSuccExceptions().size(); j++) {
|
||||
BasicBlock hd = rangeExitBlock.getSuccExceptions().get(j);
|
||||
succBlock.addSuccessorException(hd);
|
||||
|
||||
ExceptionRangeCFG rng = graph.getExceptionRange(hd, rangeExitBlock);
|
||||
rng.getProtectedRange().add(succBlock);
|
||||
}
|
||||
|
||||
// copy instructions (handler successor block)
|
||||
InstructionSequence handlerSeq = handlerBlock.getSeq();
|
||||
for(int counter = 0; counter < handler_monitorexit_index; counter++) {
|
||||
handlerSeq.addInstruction(succHandlerSeq.getInstr(0), -1);
|
||||
succHandlerSeq.removeInstruction(0);
|
||||
}
|
||||
|
||||
range_extended = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!range_extended) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void incorporateValueReturns(ControlFlowGraph graph) {
|
||||
|
||||
for (BasicBlock block : graph.getBlocks()) {
|
||||
@@ -311,8 +444,8 @@ public class DeadCodeHelper {
|
||||
|
||||
if (!block.getPreds().isEmpty()) {
|
||||
|
||||
HashSet<BasicBlock> setPredHandlersUnion = new HashSet<BasicBlock>();
|
||||
HashSet<BasicBlock> setPredHandlersIntersection = new HashSet<BasicBlock>();
|
||||
HashSet<BasicBlock> setPredHandlersUnion = new HashSet<>();
|
||||
HashSet<BasicBlock> setPredHandlersIntersection = new HashSet<>();
|
||||
|
||||
boolean firstpred = true;
|
||||
for (BasicBlock pred : block.getPreds()) {
|
||||
@@ -339,7 +472,7 @@ public class DeadCodeHelper {
|
||||
}
|
||||
|
||||
// remove redundant ranges
|
||||
HashSet<BasicBlock> setRangesToBeRemoved = new HashSet<BasicBlock>(block.getSuccExceptions());
|
||||
HashSet<BasicBlock> setRangesToBeRemoved = new HashSet<>(block.getSuccExceptions());
|
||||
setRangesToBeRemoved.removeAll(setPredHandlersUnion);
|
||||
|
||||
for (BasicBlock handler : setRangesToBeRemoved) {
|
||||
@@ -369,7 +502,7 @@ public class DeadCodeHelper {
|
||||
}
|
||||
|
||||
// remove superfluous ranges from successors
|
||||
for (BasicBlock succ : new HashSet<BasicBlock>(block.getSuccExceptions())) {
|
||||
for (BasicBlock succ : new HashSet<>(block.getSuccExceptions())) {
|
||||
if (!bpred.getSuccExceptions().contains(succ)) {
|
||||
ExceptionRangeCFG range = graph.getExceptionRange(succ, block);
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.modules.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ClasspathHelper {
|
||||
|
||||
private static final Map<String, Method> METHOD_CACHE = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
public static Method findMethod(String classname, String methodName, MethodDescriptor descriptor) {
|
||||
String targetClass = classname.replace('/', '.');
|
||||
String methodSignature = buildMethodSignature(targetClass + '.' + methodName, descriptor);
|
||||
|
||||
Method method;
|
||||
if (METHOD_CACHE.containsKey(methodSignature)) {
|
||||
method = METHOD_CACHE.get(methodSignature);
|
||||
}
|
||||
else {
|
||||
method = findMethodOnClasspath(targetClass, methodSignature);
|
||||
METHOD_CACHE.put(methodSignature, method);
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
private static Method findMethodOnClasspath(String targetClass, String methodSignature) {
|
||||
try {
|
||||
// use bootstrap classloader to only provide access to JRE classes
|
||||
Class cls = new ClassLoader(null) {}.loadClass(targetClass);
|
||||
for (Method mtd : cls.getMethods()) {
|
||||
// use contains() to ignore access modifiers and thrown exceptions
|
||||
if (mtd.toString().contains(methodSignature)) {
|
||||
return mtd;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String buildMethodSignature(String name, MethodDescriptor md) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
appendType(sb, md.ret);
|
||||
sb.append(' ').append(name).append('(');
|
||||
for (VarType param : md.params) {
|
||||
appendType(sb, param);
|
||||
sb.append(',');
|
||||
}
|
||||
if (sb.charAt(sb.length() - 1) == ',') {
|
||||
sb.setLength(sb.length() - 1);
|
||||
}
|
||||
sb.append(')');
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void appendType(StringBuilder sb, VarType type) {
|
||||
sb.append(type.value.replace('/', '.'));
|
||||
for (int i = 0; i < type.arrayDim; i++) {
|
||||
sb.append("[]");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.modules.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
|
||||
@@ -25,7 +11,7 @@ public class ClearStructHelper {
|
||||
|
||||
public static void clearStatements(RootStatement root) {
|
||||
|
||||
LinkedList<Statement> stack = new LinkedList<Statement>();
|
||||
LinkedList<Statement> stack = new LinkedList<>();
|
||||
stack.add(root);
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
|
||||
@@ -1,27 +1,17 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.modules.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
|
||||
import org.jetbrains.java.decompiler.struct.consts.PooledConstant;
|
||||
import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
|
||||
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class ConcatenationHelper {
|
||||
|
||||
@@ -52,6 +42,12 @@ public class ConcatenationHelper {
|
||||
exprTmp = iex.getInstance();
|
||||
}
|
||||
}
|
||||
else if ("makeConcatWithConstants".equals(iex.getName())) { // java 9 style
|
||||
List<Exprent> parameters = extractParameters(iex.getBootstrapArguments(), iex);
|
||||
if (parameters.size() >= 2) {
|
||||
return createConcatExprent(parameters, expr.bytecode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exprTmp == null) {
|
||||
@@ -60,7 +56,7 @@ public class ConcatenationHelper {
|
||||
|
||||
|
||||
// iterate in depth, collecting possible operands
|
||||
List<Exprent> lstOperands = new ArrayList<Exprent>();
|
||||
List<Exprent> lstOperands = new ArrayList<>();
|
||||
|
||||
while (true) {
|
||||
|
||||
@@ -125,20 +121,69 @@ public class ConcatenationHelper {
|
||||
lstOperands.set(i, rep);
|
||||
}
|
||||
}
|
||||
return createConcatExprent(lstOperands, expr.bytecode);
|
||||
}
|
||||
|
||||
private static Exprent createConcatExprent(List<Exprent> lstOperands, Set<Integer> bytecode) {
|
||||
// build exprent to return
|
||||
Exprent func = lstOperands.get(0);
|
||||
|
||||
for (int i = 1; i < lstOperands.size(); i++) {
|
||||
List<Exprent> lstTmp = new ArrayList<Exprent>();
|
||||
lstTmp.add(func);
|
||||
lstTmp.add(lstOperands.get(i));
|
||||
func = new FunctionExprent(FunctionExprent.FUNCTION_STR_CONCAT, lstTmp, expr.bytecode);
|
||||
func = new FunctionExprent(FunctionExprent.FUNCTION_STR_CONCAT, Arrays.asList(func, lstOperands.get(i)), bytecode);
|
||||
}
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
// See StringConcatFactory in jdk sources
|
||||
private static final char TAG_ARG = '\u0001';
|
||||
private static final char TAG_CONST = '\u0002';
|
||||
|
||||
private static List<Exprent> extractParameters(List<PooledConstant> bootstrapArguments, InvocationExprent expr) {
|
||||
List<Exprent> parameters = expr.getLstParameters();
|
||||
if (bootstrapArguments != null) {
|
||||
PooledConstant constant = bootstrapArguments.get(0);
|
||||
if (constant.type == CodeConstants.CONSTANT_String) {
|
||||
String recipe = ((PrimitiveConstant)constant).getString();
|
||||
|
||||
List<Exprent> res = new ArrayList<>();
|
||||
StringBuilder acc = new StringBuilder();
|
||||
int parameterId = 0;
|
||||
for (int i = 0; i < recipe.length(); i++) {
|
||||
char c = recipe.charAt(i);
|
||||
|
||||
if (c == TAG_CONST || c == TAG_ARG) {
|
||||
// Detected a special tag, flush all accumulated characters
|
||||
// as a constant first:
|
||||
if (acc.length() > 0) {
|
||||
res.add(new ConstExprent(VarType.VARTYPE_STRING, acc.toString(), expr.bytecode));
|
||||
acc.setLength(0);
|
||||
}
|
||||
if (c == TAG_CONST) {
|
||||
// skip for now
|
||||
}
|
||||
if (c == TAG_ARG) {
|
||||
res.add(parameters.get(parameterId++));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Not a special characters, this is a constant embedded into
|
||||
// the recipe itself.
|
||||
acc.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
// Flush the remaining characters as constant:
|
||||
if (acc.length() > 0) {
|
||||
res.add(new ConstExprent(VarType.VARTYPE_STRING, acc.toString(), expr.bytecode));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(parameters);
|
||||
}
|
||||
|
||||
private static boolean isAppendConcat(InvocationExprent expr, VarType cltype) {
|
||||
|
||||
if ("append".equals(expr.getName())) {
|
||||
@@ -167,13 +212,9 @@ public class ConcatenationHelper {
|
||||
}
|
||||
|
||||
private static boolean isNewConcat(NewExprent expr, VarType cltype) {
|
||||
|
||||
if (expr.getNewType().equals(cltype)) {
|
||||
VarType[] params = expr.getConstructor().getDescriptor().params;
|
||||
if (params.length == 0 || (params.length == 1 &&
|
||||
params[0].equals(VarType.VARTYPE_STRING))) {
|
||||
return true;
|
||||
}
|
||||
return params.length == 0 || params.length == 1 && params[0].equals(VarType.VARTYPE_STRING);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -1,33 +1,22 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.modules.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
public class DecHelper {
|
||||
|
||||
public static boolean checkStatementExceptions(List<Statement> lst) {
|
||||
public static boolean checkStatementExceptions(List<? extends Statement> lst) {
|
||||
|
||||
Set<Statement> all = new HashSet<Statement>(lst);
|
||||
Set<Statement> all = new HashSet<>(lst);
|
||||
|
||||
Set<Statement> handlers = new HashSet<Statement>();
|
||||
Set<Statement> handlers = new HashSet<>();
|
||||
Set<Statement> intersection = null;
|
||||
|
||||
for (Statement stat : lst) {
|
||||
@@ -37,7 +26,7 @@ public class DecHelper {
|
||||
intersection = setNew;
|
||||
}
|
||||
else {
|
||||
HashSet<Statement> interclone = new HashSet<Statement>(intersection);
|
||||
HashSet<Statement> interclone = new HashSet<>(intersection);
|
||||
interclone.removeAll(setNew);
|
||||
|
||||
intersection.retainAll(setNew);
|
||||
@@ -66,7 +55,7 @@ public class DecHelper {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isChoiceStatement(Statement head, List<Statement> lst) {
|
||||
public static boolean isChoiceStatement(Statement head, List<? super Statement> lst) {
|
||||
|
||||
Statement post = null;
|
||||
|
||||
@@ -159,7 +148,7 @@ public class DecHelper {
|
||||
if (head == statd) {
|
||||
return false;
|
||||
}
|
||||
if (!setDest.contains(statd) && post != statd) {
|
||||
if (post != statd && !setDest.contains(statd)) {
|
||||
if (post != null) {
|
||||
return false;
|
||||
}
|
||||
@@ -194,25 +183,17 @@ public class DecHelper {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static HashSet<Statement> getUniquePredExceptions(Statement head) {
|
||||
|
||||
HashSet<Statement> setHandlers = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD));
|
||||
|
||||
Iterator<Statement> it = setHandlers.iterator();
|
||||
while (it.hasNext()) {
|
||||
if (it.next().getPredecessorEdges(StatEdge.TYPE_EXCEPTION).size() > 1) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
public static Set<Statement> getUniquePredExceptions(Statement head) {
|
||||
Set<Statement> setHandlers = new HashSet<>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD));
|
||||
setHandlers.removeIf(statement -> statement.getPredecessorEdges(StatEdge.TYPE_EXCEPTION).size() > 1);
|
||||
return setHandlers;
|
||||
}
|
||||
|
||||
public static List<Exprent> copyExprentList(List<Exprent> lst) {
|
||||
List<Exprent> ret = new ArrayList<Exprent>();
|
||||
public static List<Exprent> copyExprentList(List<? extends Exprent> lst) {
|
||||
List<Exprent> ret = new ArrayList<>();
|
||||
for (Exprent expr : lst) {
|
||||
ret.add(expr.copy());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.modules.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
|
||||
@@ -35,7 +21,7 @@ public class DomHelper {
|
||||
|
||||
private static RootStatement graphToStatement(ControlFlowGraph graph) {
|
||||
|
||||
VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<Statement, Integer>();
|
||||
VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<>();
|
||||
VBStyleCollection<BasicBlock, Integer> blocks = graph.getBlocks();
|
||||
|
||||
for (BasicBlock block : blocks) {
|
||||
@@ -103,14 +89,14 @@ public class DomHelper {
|
||||
|
||||
public static VBStyleCollection<List<Integer>, Integer> calcPostDominators(Statement container) {
|
||||
|
||||
HashMap<Statement, FastFixedSet<Statement>> lists = new HashMap<Statement, FastFixedSet<Statement>>();
|
||||
HashMap<Statement, FastFixedSet<Statement>> lists = new HashMap<>();
|
||||
|
||||
StrongConnectivityHelper schelper = new StrongConnectivityHelper(container);
|
||||
List<List<Statement>> components = schelper.getComponents();
|
||||
|
||||
List<Statement> lstStats = container.getPostReversePostOrderList(StrongConnectivityHelper.getExitReps(components));
|
||||
|
||||
FastFixedSetFactory<Statement> factory = new FastFixedSetFactory<Statement>(lstStats);
|
||||
FastFixedSetFactory<Statement> factory = new FastFixedSetFactory<>(lstStats);
|
||||
|
||||
FastFixedSet<Statement> setFlagNodes = factory.spawnEmptySet();
|
||||
setFlagNodes.setAllElements();
|
||||
@@ -176,26 +162,22 @@ public class DomHelper {
|
||||
}
|
||||
while (!setFlagNodes.isEmpty());
|
||||
|
||||
VBStyleCollection<List<Integer>, Integer> ret = new VBStyleCollection<List<Integer>, Integer>();
|
||||
VBStyleCollection<List<Integer>, Integer> ret = new VBStyleCollection<>();
|
||||
List<Statement> lstRevPost = container.getReversePostOrderList(); // sort order crucial!
|
||||
|
||||
final HashMap<Integer, Integer> mapSortOrder = new HashMap<Integer, Integer>();
|
||||
final HashMap<Integer, Integer> mapSortOrder = new HashMap<>();
|
||||
for (int i = 0; i < lstRevPost.size(); i++) {
|
||||
mapSortOrder.put(lstRevPost.get(i).id, i);
|
||||
}
|
||||
|
||||
for (Statement st : lstStats) {
|
||||
|
||||
List<Integer> lstPosts = new ArrayList<Integer>();
|
||||
List<Integer> lstPosts = new ArrayList<>();
|
||||
for (Statement stt : lists.get(st)) {
|
||||
lstPosts.add(stt.id);
|
||||
}
|
||||
|
||||
Collections.sort(lstPosts, new Comparator<Integer>() {
|
||||
public int compare(Integer o1, Integer o2) {
|
||||
return mapSortOrder.get(o1).compareTo(mapSortOrder.get(o2));
|
||||
}
|
||||
});
|
||||
lstPosts.sort(Comparator.comparing(mapSortOrder::get));
|
||||
|
||||
if (lstPosts.size() > 1 && lstPosts.get(0).intValue() == st.id) {
|
||||
lstPosts.add(lstPosts.remove(0));
|
||||
@@ -211,7 +193,7 @@ public class DomHelper {
|
||||
|
||||
RootStatement root = graphToStatement(graph);
|
||||
|
||||
if (!processStatement(root, new HashMap<Integer, Set<Integer>>())) {
|
||||
if (!processStatement(root, new HashMap<>())) {
|
||||
|
||||
// try {
|
||||
// DotExporter.toDotFile(root.getFirst().getStats().get(13), new File("c:\\Temp\\stat1.dot"));
|
||||
@@ -221,7 +203,7 @@ public class DomHelper {
|
||||
throw new RuntimeException("parsing failure!");
|
||||
}
|
||||
|
||||
LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>());
|
||||
LabelHelper.lowContinueLabels(root, new HashSet<>());
|
||||
|
||||
SequenceHelper.condenseSequences(root);
|
||||
root.buildMonitorFlags();
|
||||
@@ -291,7 +273,7 @@ public class DomHelper {
|
||||
SynchronizedStatement sync = new SynchronizedStatement(current, ca.getFirst(), ca.getHandler());
|
||||
sync.setAllParent();
|
||||
|
||||
for (StatEdge edge : new HashSet<StatEdge>(ca.getLabelEdges())) {
|
||||
for (StatEdge edge : new HashSet<>(ca.getLabelEdges())) {
|
||||
sync.addLabeledEdge(edge);
|
||||
}
|
||||
|
||||
@@ -361,7 +343,7 @@ public class DomHelper {
|
||||
// DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot"));
|
||||
// } catch(Exception ex) {ex.printStackTrace();}
|
||||
|
||||
mapExtPost = new HashMap<Integer, Set<Integer>>();
|
||||
mapExtPost = new HashMap<>();
|
||||
mapRefreshed = true;
|
||||
}
|
||||
|
||||
@@ -382,7 +364,7 @@ public class DomHelper {
|
||||
Statement stat = findGeneralStatement(general, forceall, mapExtPost);
|
||||
|
||||
if (stat != null) {
|
||||
boolean complete = processStatement(stat, general.getFirst() == stat ? mapExtPost : new HashMap<Integer, Set<Integer>>());
|
||||
boolean complete = processStatement(stat, general.getFirst() == stat ? mapExtPost : new HashMap<>());
|
||||
|
||||
if (complete) {
|
||||
// replace general purpose statement with simple one
|
||||
@@ -392,7 +374,7 @@ public class DomHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
mapExtPost = new HashMap<Integer, Set<Integer>>();
|
||||
mapExtPost = new HashMap<>();
|
||||
mapRefreshed = true;
|
||||
reducibility = 0;
|
||||
}
|
||||
@@ -413,7 +395,7 @@ public class DomHelper {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
mapExtPost = new HashMap<Integer, Set<Integer>>();
|
||||
mapExtPost = new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,13 +413,13 @@ public class DomHelper {
|
||||
}
|
||||
|
||||
if (forceall) {
|
||||
vbPost = new VBStyleCollection<List<Integer>, Integer>();
|
||||
vbPost = new VBStyleCollection<>();
|
||||
List<Statement> lstAll = stat.getPostReversePostOrderList();
|
||||
|
||||
for (Statement st : lstAll) {
|
||||
Set<Integer> set = mapExtPost.get(st.id);
|
||||
if (set != null) {
|
||||
vbPost.addWithKey(new ArrayList<Integer>(set), st.id); // FIXME: sort order!!
|
||||
vbPost.addWithKey(new ArrayList<>(set), st.id); // FIXME: sort order!!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,7 +429,7 @@ public class DomHelper {
|
||||
for (Integer id : setFirst) {
|
||||
List<Integer> lst = vbPost.getWithKey(id);
|
||||
if (lst == null) {
|
||||
vbPost.addWithKey(lst = new ArrayList<Integer>(), id);
|
||||
vbPost.addWithKey(lst = new ArrayList<>(), id);
|
||||
}
|
||||
lst.add(id);
|
||||
}
|
||||
@@ -471,14 +453,12 @@ public class DomHelper {
|
||||
|
||||
Set<Integer> setExtPosts = mapExtPost.get(headid);
|
||||
|
||||
for (int i = 0; i < posts.size(); i++) {
|
||||
|
||||
Integer postid = posts.get(i);
|
||||
if (!postid.equals(headid) && !setExtPosts.contains(postid)) {
|
||||
for (Integer postId : posts) {
|
||||
if (!postId.equals(headid) && !setExtPosts.contains(postId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Statement post = stats.getWithKey(postid);
|
||||
Statement post = stats.getWithKey(postId);
|
||||
|
||||
if (post == null) { // possible in case of an inherited postdominance set
|
||||
continue;
|
||||
@@ -486,11 +466,11 @@ public class DomHelper {
|
||||
|
||||
boolean same = (post == head);
|
||||
|
||||
HashSet<Statement> setNodes = new HashSet<Statement>();
|
||||
HashSet<Statement> setPreds = new HashSet<Statement>();
|
||||
HashSet<Statement> setNodes = new HashSet<>();
|
||||
HashSet<Statement> setPreds = new HashSet<>();
|
||||
|
||||
// collect statement nodes
|
||||
HashSet<Statement> setHandlers = new HashSet<Statement>();
|
||||
HashSet<Statement> setHandlers = new HashSet<>();
|
||||
setHandlers.add(head);
|
||||
while (true) {
|
||||
|
||||
@@ -508,7 +488,7 @@ public class DomHelper {
|
||||
}
|
||||
|
||||
if (addhd) {
|
||||
LinkedList<Statement> lstStack = new LinkedList<Statement>();
|
||||
LinkedList<Statement> lstStack = new LinkedList<>();
|
||||
lstStack.add(handler);
|
||||
|
||||
while (!lstStack.isEmpty()) {
|
||||
@@ -557,14 +537,14 @@ public class DomHelper {
|
||||
|
||||
// build statement and return
|
||||
if (excok) {
|
||||
Statement res = null;
|
||||
Statement res;
|
||||
|
||||
setPreds.removeAll(setNodes);
|
||||
if (setPreds.size() == 0) {
|
||||
if ((setNodes.size() > 1 ||
|
||||
head.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).contains(head))
|
||||
&& setNodes.size() < stats.size()) {
|
||||
if (checkSynchronizedCompleteness(head, setNodes)) {
|
||||
if (checkSynchronizedCompleteness(setNodes)) {
|
||||
res = new GeneralStatement(head, setNodes, same ? null : post);
|
||||
stat.collapseNodesToStatement(res);
|
||||
|
||||
@@ -579,8 +559,7 @@ public class DomHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean checkSynchronizedCompleteness(Statement head, HashSet<Statement> setNodes) {
|
||||
|
||||
private static boolean checkSynchronizedCompleteness(Set<Statement> setNodes) {
|
||||
// check exit nodes
|
||||
for (Statement stat : setNodes) {
|
||||
if (stat.isMonitorEnter()) {
|
||||
@@ -622,26 +601,21 @@ public class DomHelper {
|
||||
|
||||
// update the postdominator map
|
||||
if (!mapExtPost.isEmpty()) {
|
||||
HashSet<Integer> setOldNodes = new HashSet<Integer>();
|
||||
HashSet<Integer> setOldNodes = new HashSet<>();
|
||||
for (Statement old : result.getStats()) {
|
||||
setOldNodes.add(old.id);
|
||||
}
|
||||
|
||||
Integer newid = result.id;
|
||||
|
||||
for (Integer key : new ArrayList<Integer>(mapExtPost.keySet())) {
|
||||
for (Integer key : new ArrayList<>(mapExtPost.keySet())) {
|
||||
Set<Integer> set = mapExtPost.get(key);
|
||||
|
||||
int oldsize = set.size();
|
||||
set.removeAll(setOldNodes);
|
||||
|
||||
if (setOldNodes.contains(key)) {
|
||||
Set<Integer> setNew = mapExtPost.get(newid);
|
||||
if (setNew == null) {
|
||||
mapExtPost.put(newid, setNew = new HashSet<Integer>());
|
||||
}
|
||||
setNew.addAll(set);
|
||||
|
||||
mapExtPost.computeIfAbsent(newid, k -> new HashSet<>()).addAll(set);
|
||||
mapExtPost.remove(key);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.modules.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class EliminateLoopsHelper {
|
||||
|
||||
|
||||
// public static boolean eliminateLoops(Statement root) {
|
||||
//
|
||||
// boolean ret = eliminateLoopsRec(root);
|
||||
//
|
||||
// if(ret) {
|
||||
// SequenceHelper.condenseSequences(root);
|
||||
//
|
||||
// HashSet<Integer> setReorderedIfs = new HashSet<Integer>();
|
||||
//
|
||||
// SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(false);
|
||||
// while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, null)) {
|
||||
// SequenceHelper.condenseSequences(root);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
private static boolean eliminateLoopsRec(Statement stat) {
|
||||
|
||||
for (Statement st : stat.getStats()) {
|
||||
if (eliminateLoopsRec(st)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (stat.type == Statement.TYPE_DO && isLoopRedundant((DoStatement)stat)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isLoopRedundant(DoStatement loop) {
|
||||
|
||||
if (loop.getLooptype() != DoStatement.LOOP_DO) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get parent loop if exists
|
||||
Statement parentloop = loop.getParent();
|
||||
while (parentloop != null && parentloop.type != Statement.TYPE_DO) {
|
||||
parentloop = parentloop.getParent();
|
||||
}
|
||||
|
||||
if (parentloop == null || parentloop.getBasichead() != loop.getBasichead()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// collect relevant break edges
|
||||
List<StatEdge> lstBreakEdges = new ArrayList<StatEdge>();
|
||||
for (StatEdge edge : loop.getLabelEdges()) {
|
||||
if (edge.getType() == StatEdge.TYPE_BREAK) { // all break edges are explicit because of LOOP_DO type
|
||||
lstBreakEdges.add(edge);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Statement loopcontent = loop.getFirst();
|
||||
|
||||
boolean firstok = loopcontent.getAllSuccessorEdges().isEmpty();
|
||||
if (!firstok) {
|
||||
StatEdge edge = loopcontent.getAllSuccessorEdges().get(0);
|
||||
firstok = (edge.closure == loop && edge.getType() == StatEdge.TYPE_BREAK);
|
||||
if (firstok) {
|
||||
lstBreakEdges.remove(edge);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!lstBreakEdges.isEmpty()) {
|
||||
if (firstok) {
|
||||
|
||||
HashMap<Integer, Boolean> statLabeled = new HashMap<Integer, Boolean>();
|
||||
List<Statement> lstEdgeClosures = new ArrayList<Statement>();
|
||||
|
||||
for (StatEdge edge : lstBreakEdges) {
|
||||
Statement minclosure = LowBreakHelper.getMinClosure(loopcontent, edge.getSource());
|
||||
lstEdgeClosures.add(minclosure);
|
||||
}
|
||||
|
||||
int precount = loop.isLabeled() ? 1 : 0;
|
||||
for (Statement st : lstEdgeClosures) {
|
||||
if (!statLabeled.containsKey(st.id)) {
|
||||
boolean btemp = st.isLabeled();
|
||||
precount += btemp ? 1 : 0;
|
||||
statLabeled.put(st.id, btemp);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < lstBreakEdges.size(); i++) {
|
||||
Statement st = lstEdgeClosures.get(i);
|
||||
statLabeled.put(st.id, LowBreakHelper.isBreakEdgeLabeled(lstBreakEdges.get(i).getSource(), st) | statLabeled.get(st.id));
|
||||
}
|
||||
|
||||
for (int i = 0; i < lstBreakEdges.size(); i++) {
|
||||
lstEdgeClosures.set(i, getMaxBreakLift(lstEdgeClosures.get(i), lstBreakEdges.get(i), statLabeled, loop));
|
||||
}
|
||||
|
||||
statLabeled.clear();
|
||||
for (Statement st : lstEdgeClosures) {
|
||||
statLabeled.put(st.id, st.isLabeled());
|
||||
}
|
||||
|
||||
for (int i = 0; i < lstBreakEdges.size(); i++) {
|
||||
Statement st = lstEdgeClosures.get(i);
|
||||
statLabeled.put(st.id, LowBreakHelper.isBreakEdgeLabeled(lstBreakEdges.get(i).getSource(), st) | statLabeled.get(st.id));
|
||||
}
|
||||
|
||||
int postcount = 0;
|
||||
for (Boolean val : statLabeled.values()) {
|
||||
postcount += val ? 1 : 0;
|
||||
}
|
||||
|
||||
if (precount <= postcount) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < lstBreakEdges.size(); i++) {
|
||||
lstEdgeClosures.get(i).addLabeledEdge(lstBreakEdges.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
eliminateLoop(loop, parentloop);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Statement getMaxBreakLift(Statement stat, StatEdge edge, HashMap<Integer, Boolean> statLabeled, Statement max) {
|
||||
|
||||
Statement closure = stat;
|
||||
Statement newclosure = stat;
|
||||
|
||||
while ((newclosure = getNextBreakLift(newclosure, edge, statLabeled, max)) != null) {
|
||||
closure = newclosure;
|
||||
}
|
||||
|
||||
return closure;
|
||||
}
|
||||
|
||||
private static Statement getNextBreakLift(Statement stat, StatEdge edge, HashMap<Integer, Boolean> statLabeled, Statement max) {
|
||||
|
||||
Statement closure = stat.getParent();
|
||||
|
||||
while (closure != null && closure != max && !closure.containsStatementStrict(edge.getDestination())) {
|
||||
|
||||
boolean edge_labeled = LowBreakHelper.isBreakEdgeLabeled(edge.getSource(), closure);
|
||||
boolean stat_labeled = statLabeled.containsKey(closure.id) ? statLabeled.get(closure.id) : closure.isLabeled();
|
||||
|
||||
if (stat_labeled || !edge_labeled) {
|
||||
return closure;
|
||||
}
|
||||
|
||||
closure = closure.getParent();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void eliminateLoop(Statement loop, Statement parentloop) {
|
||||
|
||||
// move continue edges to the parent loop
|
||||
List<StatEdge> lst = new ArrayList<StatEdge>(loop.getLabelEdges());
|
||||
for (StatEdge edge : lst) {
|
||||
loop.removePredecessor(edge);
|
||||
edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, parentloop);
|
||||
parentloop.addPredecessor(edge);
|
||||
|
||||
parentloop.addLabeledEdge(edge);
|
||||
}
|
||||
|
||||
// remove the last break edge, if exists
|
||||
Statement loopcontent = loop.getFirst();
|
||||
if (!loopcontent.getAllSuccessorEdges().isEmpty()) {
|
||||
loopcontent.removeSuccessor(loopcontent.getAllSuccessorEdges().get(0));
|
||||
}
|
||||
|
||||
// replace loop with its content
|
||||
loop.getParent().replaceStatement(loop, loopcontent);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.modules.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
|
||||
@@ -21,7 +7,6 @@ import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
|
||||
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -29,32 +14,23 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class ExitHelper {
|
||||
|
||||
|
||||
public static boolean condenseExits(RootStatement root) {
|
||||
|
||||
int changed = integrateExits(root);
|
||||
|
||||
if (changed > 0) {
|
||||
|
||||
cleanUpUnreachableBlocks(root);
|
||||
|
||||
SequenceHelper.condenseSequences(root);
|
||||
}
|
||||
|
||||
return (changed > 0);
|
||||
}
|
||||
|
||||
|
||||
private static void cleanUpUnreachableBlocks(Statement stat) {
|
||||
|
||||
boolean found;
|
||||
do {
|
||||
|
||||
found = false;
|
||||
|
||||
for (int i = 0; i < stat.getStats().size(); i++) {
|
||||
|
||||
Statement st = stat.getStats().get(i);
|
||||
|
||||
cleanUpUnreachableBlocks(st);
|
||||
@@ -71,7 +47,7 @@ public class ExitHelper {
|
||||
set.remove(secondlast);
|
||||
|
||||
if (set.isEmpty()) {
|
||||
last.setExprents(new ArrayList<Exprent>());
|
||||
last.setExprents(new ArrayList<>());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@@ -83,16 +59,12 @@ public class ExitHelper {
|
||||
while (found);
|
||||
}
|
||||
|
||||
|
||||
private static int integrateExits(Statement stat) {
|
||||
|
||||
int ret = 0;
|
||||
Statement dest = null;
|
||||
Statement dest;
|
||||
|
||||
if (stat.getExprents() == null) {
|
||||
|
||||
while (true) {
|
||||
|
||||
int changed = 0;
|
||||
|
||||
for (Statement st : stat.getStats()) {
|
||||
@@ -108,33 +80,31 @@ public class ExitHelper {
|
||||
}
|
||||
}
|
||||
|
||||
if (stat.type == Statement.TYPE_IF) {
|
||||
IfStatement ifst = (IfStatement)stat;
|
||||
if (ifst.getIfstat() == null) {
|
||||
StatEdge ifedge = ifst.getIfEdge();
|
||||
dest = isExitEdge(ifedge);
|
||||
if (dest != null) {
|
||||
BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
|
||||
DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
|
||||
bstat.setExprents(DecHelper.copyExprentList(dest.getExprents()));
|
||||
|
||||
switch (stat.type) {
|
||||
case Statement.TYPE_IF:
|
||||
IfStatement ifst = (IfStatement)stat;
|
||||
if (ifst.getIfstat() == null) {
|
||||
StatEdge ifedge = ifst.getIfEdge();
|
||||
dest = isExitEdge(ifedge);
|
||||
if (dest != null) {
|
||||
BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
|
||||
DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
|
||||
bstat.setExprents(DecHelper.copyExprentList(dest.getExprents()));
|
||||
ifst.getFirst().removeSuccessor(ifedge);
|
||||
StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, ifst.getFirst(), bstat);
|
||||
ifst.getFirst().addSuccessor(newedge);
|
||||
ifst.setIfEdge(newedge);
|
||||
ifst.setIfstat(bstat);
|
||||
ifst.getStats().addWithKey(bstat, bstat.id);
|
||||
bstat.setParent(ifst);
|
||||
|
||||
ifst.getFirst().removeSuccessor(ifedge);
|
||||
StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, ifst.getFirst(), bstat);
|
||||
ifst.getFirst().addSuccessor(newedge);
|
||||
ifst.setIfEdge(newedge);
|
||||
ifst.setIfstat(bstat);
|
||||
ifst.getStats().addWithKey(bstat, bstat.id);
|
||||
bstat.setParent(ifst);
|
||||
|
||||
StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0);
|
||||
StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination());
|
||||
bstat.addSuccessor(newexitedge);
|
||||
oldexitedge.closure.addLabeledEdge(newexitedge);
|
||||
ret = 1;
|
||||
}
|
||||
StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0);
|
||||
StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination());
|
||||
bstat.addSuccessor(newexitedge);
|
||||
oldexitedge.closure.addLabeledEdge(newexitedge);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,15 +138,12 @@ public class ExitHelper {
|
||||
// LabelHelper.lowContinueLabels(block, new HashSet<StatEdge>());
|
||||
// do it by hand
|
||||
for (StatEdge prededge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) {
|
||||
|
||||
block.removePredecessor(prededge);
|
||||
prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, stat);
|
||||
stat.addPredecessor(prededge);
|
||||
|
||||
stat.addLabeledEdge(prededge);
|
||||
}
|
||||
|
||||
|
||||
stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, bstat));
|
||||
|
||||
for (StatEdge edge : dest.getAllPredecessorEdges()) {
|
||||
@@ -202,11 +169,9 @@ public class ExitHelper {
|
||||
}
|
||||
|
||||
private static Statement isExitEdge(StatEdge edge) {
|
||||
|
||||
Statement dest = edge.getDestination();
|
||||
|
||||
if (edge.getType() == StatEdge.TYPE_BREAK && dest.type == Statement.TYPE_BASICBLOCK
|
||||
&& edge.explicit && (edge.labeled || isOnlyEdge(edge))) {
|
||||
if (edge.getType() == StatEdge.TYPE_BREAK && dest.type == Statement.TYPE_BASICBLOCK && edge.explicit && (edge.labeled || isOnlyEdge(edge))) {
|
||||
List<Exprent> data = dest.getExprents();
|
||||
|
||||
if (data != null && data.size() == 1) {
|
||||
@@ -220,7 +185,6 @@ public class ExitHelper {
|
||||
}
|
||||
|
||||
private static boolean isOnlyEdge(StatEdge edge) {
|
||||
|
||||
Statement stat = edge.getDestination();
|
||||
|
||||
for (StatEdge ed : stat.getAllPredecessorEdges()) {
|
||||
@@ -243,10 +207,7 @@ public class ExitHelper {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean removeRedundantReturns(RootStatement root) {
|
||||
|
||||
boolean res = false;
|
||||
|
||||
public static void removeRedundantReturns(RootStatement root) {
|
||||
DummyExitStatement dummyExit = root.getDummyExit();
|
||||
|
||||
for (StatEdge edge : dummyExit.getAllPredecessorEdges()) {
|
||||
@@ -261,88 +222,10 @@ public class ExitHelper {
|
||||
// remove redundant return
|
||||
dummyExit.addBytecodeOffsets(ex.bytecode);
|
||||
lstExpr.remove(lstExpr.size() - 1);
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static boolean handleReturnFromInitializer(RootStatement root) {
|
||||
|
||||
boolean res = false;
|
||||
|
||||
Statement exit = root.getDummyExit();
|
||||
Statement top = root.getFirst();
|
||||
Statement newret = null;
|
||||
|
||||
boolean sharedcreated = false;
|
||||
|
||||
for (StatEdge edge : exit.getAllPredecessorEdges()) {
|
||||
if (edge.explicit) {
|
||||
|
||||
if (!sharedcreated) {
|
||||
newret = addSharedInitializerReturn(root);
|
||||
sharedcreated = true;
|
||||
}
|
||||
|
||||
Statement source = edge.getSource();
|
||||
List<Exprent> lstExpr = source.getExprents();
|
||||
if (lstExpr != null && !lstExpr.isEmpty()) {
|
||||
Exprent expr = lstExpr.get(lstExpr.size() - 1);
|
||||
if (expr.type == Exprent.EXPRENT_EXIT) {
|
||||
ExitExprent ex = (ExitExprent)expr;
|
||||
if (ex.getExitType() == ExitExprent.EXIT_RETURN && ex.getValue() == null) {
|
||||
lstExpr.remove(lstExpr.size() - 1);
|
||||
|
||||
source.removeSuccessor(edge);
|
||||
source.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, source, newret, top));
|
||||
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static Statement addSharedInitializerReturn(RootStatement root) {
|
||||
|
||||
Statement exit = root.getDummyExit();
|
||||
Statement top = root.getFirst();
|
||||
|
||||
// build a new statement with the single instruction 'return'
|
||||
BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
|
||||
DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
|
||||
|
||||
ExitExprent retexpr = new ExitExprent(ExitExprent.EXIT_RETURN, null,
|
||||
((MethodDescriptor)DecompilerContext
|
||||
.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR)).ret, null);
|
||||
// a changeable list needed
|
||||
bstat.setExprents(new ArrayList<Exprent>(Arrays.asList(new Exprent[]{retexpr})));
|
||||
|
||||
// build sequence to replace the former top statement
|
||||
SequenceStatement seq = new SequenceStatement(Arrays.asList(top, bstat));
|
||||
top.setParent(seq);
|
||||
bstat.setParent(seq);
|
||||
seq.setParent(root);
|
||||
|
||||
root.getStats().removeWithKey(top.id);
|
||||
root.getStats().addWithKeyAndIndex(0, seq, seq.id);
|
||||
root.setFirst(seq);
|
||||
|
||||
for (StatEdge succedge : top.getAllSuccessorEdges()) {
|
||||
top.removeSuccessor(succedge);
|
||||
}
|
||||
|
||||
top.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, top, bstat));
|
||||
bstat.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, bstat, exit, seq));
|
||||
|
||||
return bstat;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.modules.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
@@ -20,7 +6,7 @@ import org.jetbrains.java.decompiler.code.Instruction;
|
||||
import org.jetbrains.java.decompiler.code.InstructionSequence;
|
||||
import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
|
||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||
import org.jetbrains.java.decompiler.main.TextBuffer;
|
||||
import org.jetbrains.java.decompiler.util.TextBuffer;
|
||||
import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
|
||||
@@ -38,149 +24,115 @@ import org.jetbrains.java.decompiler.struct.consts.PooledConstant;
|
||||
import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
|
||||
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.util.TextUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ExprProcessor implements CodeConstants {
|
||||
|
||||
public static final String UNDEFINED_TYPE_STRING = "<undefinedtype>";
|
||||
public static final String UNKNOWN_TYPE_STRING = "<unknown>";
|
||||
public static final String NULL_TYPE_STRING = "<null>";
|
||||
|
||||
private static final HashMap<Integer, Integer> mapConsts = new HashMap<Integer, Integer>();
|
||||
|
||||
private static final Map<Integer, Integer> mapConsts = new HashMap<>();
|
||||
static {
|
||||
|
||||
// mapConsts.put(new Integer(opc_i2l), new
|
||||
// Integer(FunctionExprent.FUNCTION_I2L));
|
||||
// mapConsts.put(new Integer(opc_i2f), new
|
||||
// Integer(FunctionExprent.FUNCTION_I2F));
|
||||
// mapConsts.put(new Integer(opc_i2d), new
|
||||
// Integer(FunctionExprent.FUNCTION_I2D));
|
||||
// mapConsts.put(new Integer(opc_l2i), new
|
||||
// Integer(FunctionExprent.FUNCTION_L2I));
|
||||
// mapConsts.put(new Integer(opc_l2f), new
|
||||
// Integer(FunctionExprent.FUNCTION_L2F));
|
||||
// mapConsts.put(new Integer(opc_l2d), new
|
||||
// Integer(FunctionExprent.FUNCTION_L2D));
|
||||
// mapConsts.put(new Integer(opc_f2i), new
|
||||
// Integer(FunctionExprent.FUNCTION_F2I));
|
||||
// mapConsts.put(new Integer(opc_f2l), new
|
||||
// Integer(FunctionExprent.FUNCTION_F2L));
|
||||
// mapConsts.put(new Integer(opc_f2d), new
|
||||
// Integer(FunctionExprent.FUNCTION_F2D));
|
||||
// mapConsts.put(new Integer(opc_d2i), new
|
||||
// Integer(FunctionExprent.FUNCTION_D2I));
|
||||
// mapConsts.put(new Integer(opc_d2l), new
|
||||
// Integer(FunctionExprent.FUNCTION_D2L));
|
||||
// mapConsts.put(new Integer(opc_d2f), new
|
||||
// Integer(FunctionExprent.FUNCTION_D2F));
|
||||
// mapConsts.put(new Integer(opc_i2b), new
|
||||
// Integer(FunctionExprent.FUNCTION_I2B));
|
||||
// mapConsts.put(new Integer(opc_i2c), new
|
||||
// Integer(FunctionExprent.FUNCTION_I2C));
|
||||
// mapConsts.put(new Integer(opc_i2s), new
|
||||
// Integer(FunctionExprent.FUNCTION_I2S));
|
||||
|
||||
mapConsts.put(new Integer(opc_arraylength), new Integer(FunctionExprent.FUNCTION_ARRAY_LENGTH));
|
||||
mapConsts.put(new Integer(opc_checkcast), new Integer(FunctionExprent.FUNCTION_CAST));
|
||||
mapConsts.put(new Integer(opc_instanceof), new Integer(FunctionExprent.FUNCTION_INSTANCEOF));
|
||||
mapConsts.put(opc_arraylength, FunctionExprent.FUNCTION_ARRAY_LENGTH);
|
||||
mapConsts.put(opc_checkcast, FunctionExprent.FUNCTION_CAST);
|
||||
mapConsts.put(opc_instanceof, FunctionExprent.FUNCTION_INSTANCEOF);
|
||||
}
|
||||
|
||||
private static final VarType[] consts =
|
||||
new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_CLASS,
|
||||
VarType.VARTYPE_STRING};
|
||||
private static final VarType[] consts = {
|
||||
VarType.VARTYPE_INT, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_CLASS, VarType.VARTYPE_STRING
|
||||
};
|
||||
|
||||
private static final VarType[] vartypes =
|
||||
new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT};
|
||||
private static final VarType[] varTypes = {
|
||||
VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT
|
||||
};
|
||||
|
||||
private static final VarType[] arrtypes =
|
||||
new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT,
|
||||
VarType.VARTYPE_BOOLEAN, VarType.VARTYPE_CHAR, VarType.VARTYPE_SHORT};
|
||||
private static final VarType[] arrTypes = {
|
||||
VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT,
|
||||
VarType.VARTYPE_BOOLEAN, VarType.VARTYPE_CHAR, VarType.VARTYPE_SHORT
|
||||
};
|
||||
|
||||
private static final int[] func1 =
|
||||
new int[]{FunctionExprent.FUNCTION_ADD, FunctionExprent.FUNCTION_SUB, FunctionExprent.FUNCTION_MUL, FunctionExprent.FUNCTION_DIV,
|
||||
FunctionExprent.FUNCTION_REM};
|
||||
private static final int[] func1 = {
|
||||
FunctionExprent.FUNCTION_ADD, FunctionExprent.FUNCTION_SUB, FunctionExprent.FUNCTION_MUL, FunctionExprent.FUNCTION_DIV,
|
||||
FunctionExprent.FUNCTION_REM
|
||||
};
|
||||
private static final int[] func2 = {
|
||||
FunctionExprent.FUNCTION_SHL, FunctionExprent.FUNCTION_SHR, FunctionExprent.FUNCTION_USHR, FunctionExprent.FUNCTION_AND,
|
||||
FunctionExprent.FUNCTION_OR, FunctionExprent.FUNCTION_XOR
|
||||
};
|
||||
private static final int[] func3 = {
|
||||
FunctionExprent.FUNCTION_I2L, FunctionExprent.FUNCTION_I2F, FunctionExprent.FUNCTION_I2D, FunctionExprent.FUNCTION_L2I,
|
||||
FunctionExprent.FUNCTION_L2F, FunctionExprent.FUNCTION_L2D, FunctionExprent.FUNCTION_F2I, FunctionExprent.FUNCTION_F2L,
|
||||
FunctionExprent.FUNCTION_F2D, FunctionExprent.FUNCTION_D2I, FunctionExprent.FUNCTION_D2L, FunctionExprent.FUNCTION_D2F,
|
||||
FunctionExprent.FUNCTION_I2B, FunctionExprent.FUNCTION_I2C, FunctionExprent.FUNCTION_I2S
|
||||
};
|
||||
private static final int[] func4 = {
|
||||
FunctionExprent.FUNCTION_LCMP, FunctionExprent.FUNCTION_FCMPL, FunctionExprent.FUNCTION_FCMPG, FunctionExprent.FUNCTION_DCMPL,
|
||||
FunctionExprent.FUNCTION_DCMPG
|
||||
};
|
||||
private static final int[] func5 = {
|
||||
IfExprent.IF_EQ, IfExprent.IF_NE, IfExprent.IF_LT, IfExprent.IF_GE, IfExprent.IF_GT, IfExprent.IF_LE
|
||||
};
|
||||
private static final int[] func6 = {
|
||||
IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPGT, IfExprent.IF_ICMPLE,
|
||||
IfExprent.IF_ACMPEQ, IfExprent.IF_ACMPNE
|
||||
};
|
||||
private static final int[] func7 = {IfExprent.IF_NULL, IfExprent.IF_NONNULL};
|
||||
private static final int[] func8 = {MonitorExprent.MONITOR_ENTER, MonitorExprent.MONITOR_EXIT};
|
||||
|
||||
private static final int[] func2 =
|
||||
new int[]{FunctionExprent.FUNCTION_SHL, FunctionExprent.FUNCTION_SHR, FunctionExprent.FUNCTION_USHR, FunctionExprent.FUNCTION_AND,
|
||||
FunctionExprent.FUNCTION_OR, FunctionExprent.FUNCTION_XOR};
|
||||
private static final int[] arrTypeIds = {
|
||||
CodeConstants.TYPE_BOOLEAN, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_DOUBLE,
|
||||
CodeConstants.TYPE_BYTE, CodeConstants.TYPE_SHORT, CodeConstants.TYPE_INT, CodeConstants.TYPE_LONG
|
||||
};
|
||||
|
||||
private static final int[] func3 =
|
||||
new int[]{FunctionExprent.FUNCTION_I2L, FunctionExprent.FUNCTION_I2F, FunctionExprent.FUNCTION_I2D, FunctionExprent.FUNCTION_L2I,
|
||||
FunctionExprent.FUNCTION_L2F, FunctionExprent.FUNCTION_L2D, FunctionExprent.FUNCTION_F2I, FunctionExprent.FUNCTION_F2L,
|
||||
FunctionExprent.FUNCTION_F2D,
|
||||
FunctionExprent.FUNCTION_D2I, FunctionExprent.FUNCTION_D2L, FunctionExprent.FUNCTION_D2F, FunctionExprent.FUNCTION_I2B,
|
||||
FunctionExprent.FUNCTION_I2C,
|
||||
FunctionExprent.FUNCTION_I2S};
|
||||
private static final int[] negIfs = {
|
||||
IfExprent.IF_NE, IfExprent.IF_EQ, IfExprent.IF_GE, IfExprent.IF_LT, IfExprent.IF_LE, IfExprent.IF_GT, IfExprent.IF_NONNULL,
|
||||
IfExprent.IF_NULL, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPLE,
|
||||
IfExprent.IF_ICMPGT, IfExprent.IF_ACMPNE, IfExprent.IF_ACMPEQ
|
||||
};
|
||||
|
||||
private static final int[] func4 =
|
||||
new int[]{FunctionExprent.FUNCTION_LCMP, FunctionExprent.FUNCTION_FCMPL, FunctionExprent.FUNCTION_FCMPG, FunctionExprent.FUNCTION_DCMPL,
|
||||
FunctionExprent.FUNCTION_DCMPG};
|
||||
private static final String[] typeNames = {"byte", "char", "double", "float", "int", "long", "short", "boolean"};
|
||||
|
||||
private static final int[] func5 =
|
||||
new int[]{IfExprent.IF_EQ, IfExprent.IF_NE, IfExprent.IF_LT, IfExprent.IF_GE, IfExprent.IF_GT, IfExprent.IF_LE};
|
||||
private final MethodDescriptor methodDescriptor;
|
||||
private final VarProcessor varProcessor;
|
||||
|
||||
private static final int[] func6 =
|
||||
new int[]{IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPGT, IfExprent.IF_ICMPLE,
|
||||
IfExprent.IF_ACMPEQ, IfExprent.IF_ACMPNE};
|
||||
|
||||
private static final int[] func7 = new int[]{IfExprent.IF_NULL, IfExprent.IF_NONNULL};
|
||||
|
||||
private static final int[] func8 = new int[]{MonitorExprent.MONITOR_ENTER, MonitorExprent.MONITOR_EXIT};
|
||||
|
||||
private static final int[] arr_type =
|
||||
new int[]{CodeConstants.TYPE_BOOLEAN, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_DOUBLE,
|
||||
CodeConstants.TYPE_BYTE, CodeConstants.TYPE_SHORT, CodeConstants.TYPE_INT, CodeConstants.TYPE_LONG};
|
||||
|
||||
private static final int[] negifs =
|
||||
new int[]{IfExprent.IF_NE, IfExprent.IF_EQ, IfExprent.IF_GE, IfExprent.IF_LT, IfExprent.IF_LE, IfExprent.IF_GT, IfExprent.IF_NONNULL,
|
||||
IfExprent.IF_NULL, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPLE,
|
||||
IfExprent.IF_ICMPGT, IfExprent.IF_ACMPNE,
|
||||
IfExprent.IF_ACMPEQ};
|
||||
|
||||
private static final String[] typeNames = new String[]{"byte", "char", "double", "float", "int", "long", "short", "boolean",};
|
||||
|
||||
private final VarProcessor varProcessor = (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR);
|
||||
public ExprProcessor(MethodDescriptor md, VarProcessor varProc) {
|
||||
methodDescriptor = md;
|
||||
varProcessor = varProc;
|
||||
}
|
||||
|
||||
public void processStatement(RootStatement root, StructClass cl) {
|
||||
|
||||
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
|
||||
DirectGraph dgraph = flatthelper.buildDirectGraph(root);
|
||||
|
||||
// try {
|
||||
// DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot"));
|
||||
// } catch (Exception ex) {
|
||||
// ex.printStackTrace();
|
||||
// }
|
||||
|
||||
// collect finally entry points
|
||||
Set<String> setFinallyShortRangeEntryPoints = new HashSet<String>();
|
||||
Set<String> setFinallyShortRangeEntryPoints = new HashSet<>();
|
||||
for (List<FinallyPathWrapper> lst : dgraph.mapShortRangeFinallyPaths.values()) {
|
||||
for (FinallyPathWrapper finwrap : lst) {
|
||||
setFinallyShortRangeEntryPoints.add(finwrap.entry);
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> setFinallyLongRangeEntryPaths = new HashSet<String>();
|
||||
Set<String> setFinallyLongRangeEntryPaths = new HashSet<>();
|
||||
for (List<FinallyPathWrapper> lst : dgraph.mapLongRangeFinallyPaths.values()) {
|
||||
for (FinallyPathWrapper finwrap : lst) {
|
||||
setFinallyLongRangeEntryPaths.add(finwrap.source + "##" + finwrap.entry);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, VarExprent> mapCatch = new HashMap<String, VarExprent>();
|
||||
Map<String, VarExprent> mapCatch = new HashMap<>();
|
||||
collectCatchVars(root, flatthelper, mapCatch);
|
||||
|
||||
Map<DirectNode, Map<String, PrimitiveExprsList>> mapData = new HashMap<DirectNode, Map<String, PrimitiveExprsList>>();
|
||||
Map<DirectNode, Map<String, PrimitiveExprsList>> mapData = new HashMap<>();
|
||||
|
||||
LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
|
||||
LinkedList<LinkedList<String>> stackEntryPoint = new LinkedList<LinkedList<String>>();
|
||||
LinkedList<DirectNode> stack = new LinkedList<>();
|
||||
LinkedList<LinkedList<String>> stackEntryPoint = new LinkedList<>();
|
||||
|
||||
stack.add(dgraph.first);
|
||||
stackEntryPoint.add(new LinkedList<String>());
|
||||
stackEntryPoint.add(new LinkedList<>());
|
||||
|
||||
Map<String, PrimitiveExprsList> map = new HashMap<String, PrimitiveExprsList>();
|
||||
Map<String, PrimitiveExprsList> map = new HashMap<>();
|
||||
map.put(null, new PrimitiveExprsList());
|
||||
mapData.put(dgraph.first, map);
|
||||
|
||||
@@ -219,13 +171,8 @@ public class ExprProcessor implements CodeConstants {
|
||||
}
|
||||
|
||||
if (isSuccessor) {
|
||||
|
||||
Map<String, PrimitiveExprsList> mapSucc = mapData.get(nd);
|
||||
if (mapSucc == null) {
|
||||
mapData.put(nd, mapSucc = new HashMap<String, PrimitiveExprsList>());
|
||||
}
|
||||
|
||||
LinkedList<String> ndentrypoints = new LinkedList<String>(entrypoints);
|
||||
Map<String, PrimitiveExprsList> mapSucc = mapData.computeIfAbsent(nd, k -> new HashMap<>());
|
||||
LinkedList<String> ndentrypoints = new LinkedList<>(entrypoints);
|
||||
|
||||
if (setFinallyLongRangeEntryPaths.contains(node.id + "##" + nd.id)) {
|
||||
ndentrypoints.addLast(node.id);
|
||||
@@ -322,8 +269,7 @@ public class ExprProcessor implements CodeConstants {
|
||||
public void processBlock(BasicBlockStatement stat, PrimitiveExprsList data, StructClass cl) {
|
||||
|
||||
ConstantPool pool = cl.getPool();
|
||||
StructBootstrapMethodsAttribute bootstrap =
|
||||
(StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
|
||||
StructBootstrapMethodsAttribute bootstrap = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
|
||||
|
||||
BasicBlock block = stat.getBlock();
|
||||
|
||||
@@ -344,31 +290,31 @@ public class ExprProcessor implements CodeConstants {
|
||||
break;
|
||||
case opc_bipush:
|
||||
case opc_sipush:
|
||||
pushEx(stack, exprlist, new ConstExprent(instr.getOperand(0), true, bytecode_offsets));
|
||||
pushEx(stack, exprlist, new ConstExprent(instr.operand(0), true, bytecode_offsets));
|
||||
break;
|
||||
case opc_lconst_0:
|
||||
case opc_lconst_1:
|
||||
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_LONG, new Long(instr.opcode - opc_lconst_0), bytecode_offsets));
|
||||
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_LONG, (long)(instr.opcode - opc_lconst_0), bytecode_offsets));
|
||||
break;
|
||||
case opc_fconst_0:
|
||||
case opc_fconst_1:
|
||||
case opc_fconst_2:
|
||||
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_FLOAT, new Float(instr.opcode - opc_fconst_0), bytecode_offsets));
|
||||
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_FLOAT, (float)(instr.opcode - opc_fconst_0), bytecode_offsets));
|
||||
break;
|
||||
case opc_dconst_0:
|
||||
case opc_dconst_1:
|
||||
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(instr.opcode - opc_dconst_0), bytecode_offsets));
|
||||
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_DOUBLE, (double)(instr.opcode - opc_dconst_0), bytecode_offsets));
|
||||
break;
|
||||
case opc_ldc:
|
||||
case opc_ldc_w:
|
||||
case opc_ldc2_w:
|
||||
PooledConstant cn = pool.getConstant(instr.getOperand(0));
|
||||
PooledConstant cn = pool.getConstant(instr.operand(0));
|
||||
if (cn instanceof PrimitiveConstant) {
|
||||
pushEx(stack, exprlist, new ConstExprent(consts[cn.type - CONSTANT_Integer], ((PrimitiveConstant)cn).value, bytecode_offsets));
|
||||
}
|
||||
else if (cn instanceof LinkConstant) {
|
||||
//TODO: for now treat Links as Strings
|
||||
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_STRING, ((LinkConstant)cn).elementname , bytecode_offsets));
|
||||
pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_STRING, ((LinkConstant)cn).elementname, bytecode_offsets));
|
||||
}
|
||||
break;
|
||||
case opc_iload:
|
||||
@@ -376,7 +322,7 @@ public class ExprProcessor implements CodeConstants {
|
||||
case opc_fload:
|
||||
case opc_dload:
|
||||
case opc_aload:
|
||||
pushEx(stack, exprlist, new VarExprent(instr.getOperand(0), vartypes[instr.opcode - opc_iload], varProcessor));
|
||||
pushEx(stack, exprlist, new VarExprent(instr.operand(0), varTypes[instr.opcode - opc_iload], varProcessor, bytecode_offset));
|
||||
break;
|
||||
case opc_iaload:
|
||||
case opc_laload:
|
||||
@@ -397,17 +343,17 @@ public class ExprProcessor implements CodeConstants {
|
||||
case opc_daload:
|
||||
vartype = VarType.VARTYPE_DOUBLE;
|
||||
}
|
||||
pushEx(stack, exprlist, new ArrayExprent(arr, index, arrtypes[instr.opcode - opc_iaload], bytecode_offsets), vartype);
|
||||
pushEx(stack, exprlist, new ArrayExprent(arr, index, arrTypes[instr.opcode - opc_iaload], bytecode_offsets), vartype);
|
||||
break;
|
||||
case opc_istore:
|
||||
case opc_lstore:
|
||||
case opc_fstore:
|
||||
case opc_dstore:
|
||||
case opc_astore:
|
||||
Exprent top = stack.pop();
|
||||
int varindex = instr.getOperand(0);
|
||||
AssignmentExprent assign =
|
||||
new AssignmentExprent(new VarExprent(varindex, vartypes[instr.opcode - opc_istore], varProcessor), top, bytecode_offsets);
|
||||
Exprent expr = stack.pop();
|
||||
int varindex = instr.operand(0);
|
||||
AssignmentExprent assign = new AssignmentExprent(
|
||||
new VarExprent(varindex, varTypes[instr.opcode - opc_istore], varProcessor, nextMeaningfulOffset(block, i)), expr, bytecode_offsets);
|
||||
exprlist.add(assign);
|
||||
break;
|
||||
case opc_iastore:
|
||||
@@ -422,7 +368,8 @@ public class ExprProcessor implements CodeConstants {
|
||||
Exprent index_store = stack.pop();
|
||||
Exprent arr_store = stack.pop();
|
||||
AssignmentExprent arrassign =
|
||||
new AssignmentExprent(new ArrayExprent(arr_store, index_store, arrtypes[instr.opcode - opc_iastore], bytecode_offsets), value, bytecode_offsets);
|
||||
new AssignmentExprent(new ArrayExprent(arr_store, index_store, arrTypes[instr.opcode - opc_iastore], bytecode_offsets), value,
|
||||
bytecode_offsets);
|
||||
exprlist.add(arrassign);
|
||||
break;
|
||||
case opc_iadd:
|
||||
@@ -468,10 +415,10 @@ public class ExprProcessor implements CodeConstants {
|
||||
pushEx(stack, exprlist, new FunctionExprent(FunctionExprent.FUNCTION_NEG, stack, bytecode_offsets));
|
||||
break;
|
||||
case opc_iinc:
|
||||
VarExprent vevar = new VarExprent(instr.getOperand(0), VarType.VARTYPE_INT, varProcessor);
|
||||
VarExprent vevar = new VarExprent(instr.operand(0), VarType.VARTYPE_INT, varProcessor);
|
||||
exprlist.add(new AssignmentExprent(vevar, new FunctionExprent(
|
||||
instr.getOperand(1) < 0 ? FunctionExprent.FUNCTION_SUB : FunctionExprent.FUNCTION_ADD, Arrays
|
||||
.asList(vevar.copy(), new ConstExprent(VarType.VARTYPE_INT, Math.abs(instr.getOperand(1)), null)),
|
||||
instr.operand(1) < 0 ? FunctionExprent.FUNCTION_SUB : FunctionExprent.FUNCTION_ADD, Arrays
|
||||
.asList(vevar.copy(), new ConstExprent(VarType.VARTYPE_INT, Math.abs(instr.operand(1)), null)),
|
||||
bytecode_offsets), bytecode_offsets));
|
||||
break;
|
||||
case opc_i2l:
|
||||
@@ -504,7 +451,7 @@ public class ExprProcessor implements CodeConstants {
|
||||
case opc_ifge:
|
||||
case opc_ifgt:
|
||||
case opc_ifle:
|
||||
exprlist.add(new IfExprent(negifs[func5[instr.opcode - opc_ifeq]], stack, bytecode_offsets));
|
||||
exprlist.add(new IfExprent(negIfs[func5[instr.opcode - opc_ifeq]], stack, bytecode_offsets));
|
||||
break;
|
||||
case opc_if_icmpeq:
|
||||
case opc_if_icmpne:
|
||||
@@ -514,16 +461,16 @@ public class ExprProcessor implements CodeConstants {
|
||||
case opc_if_icmple:
|
||||
case opc_if_acmpeq:
|
||||
case opc_if_acmpne:
|
||||
exprlist.add(new IfExprent(negifs[func6[instr.opcode - opc_if_icmpeq]], stack, bytecode_offsets));
|
||||
exprlist.add(new IfExprent(negIfs[func6[instr.opcode - opc_if_icmpeq]], stack, bytecode_offsets));
|
||||
break;
|
||||
case opc_ifnull:
|
||||
case opc_ifnonnull:
|
||||
exprlist.add(new IfExprent(negifs[func7[instr.opcode - opc_ifnull]], stack, bytecode_offsets));
|
||||
exprlist.add(new IfExprent(negIfs[func7[instr.opcode - opc_ifnull]], stack, bytecode_offsets));
|
||||
break;
|
||||
case opc_tableswitch:
|
||||
case opc_lookupswitch:
|
||||
exprlist.add(new SwitchExprent(stack.pop(), bytecode_offsets));
|
||||
break;
|
||||
break;
|
||||
case opc_ireturn:
|
||||
case opc_lreturn:
|
||||
case opc_freturn:
|
||||
@@ -533,32 +480,31 @@ public class ExprProcessor implements CodeConstants {
|
||||
case opc_athrow:
|
||||
exprlist.add(new ExitExprent(instr.opcode == opc_athrow ? ExitExprent.EXIT_THROW : ExitExprent.EXIT_RETURN,
|
||||
instr.opcode == opc_return ? null : stack.pop(),
|
||||
instr.opcode == opc_athrow
|
||||
? null
|
||||
: ((MethodDescriptor)DecompilerContext
|
||||
.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR)).ret,
|
||||
instr.opcode == opc_athrow ? null : methodDescriptor.ret,
|
||||
bytecode_offsets));
|
||||
break;
|
||||
break;
|
||||
case opc_monitorenter:
|
||||
case opc_monitorexit:
|
||||
exprlist.add(new MonitorExprent(func8[instr.opcode - opc_monitorenter], stack.pop(), bytecode_offsets));
|
||||
break;
|
||||
case opc_checkcast:
|
||||
case opc_instanceof:
|
||||
stack.push(new ConstExprent(new VarType(pool.getPrimitiveConstant(instr.getOperand(0)).getString(), true), null, null));
|
||||
stack.push(new ConstExprent(new VarType(pool.getPrimitiveConstant(instr.operand(0)).getString(), true), null, null));
|
||||
case opc_arraylength:
|
||||
pushEx(stack, exprlist, new FunctionExprent(mapConsts.get(instr.opcode).intValue(), stack, bytecode_offsets));
|
||||
pushEx(stack, exprlist, new FunctionExprent(mapConsts.get(instr.opcode), stack, bytecode_offsets));
|
||||
break;
|
||||
case opc_getstatic:
|
||||
case opc_getfield:
|
||||
pushEx(stack, exprlist,
|
||||
new FieldExprent(pool.getLinkConstant(instr.getOperand(0)), instr.opcode == opc_getstatic ? null : stack.pop(), bytecode_offsets));
|
||||
new FieldExprent(pool.getLinkConstant(instr.operand(0)), instr.opcode == opc_getstatic ? null : stack.pop(),
|
||||
bytecode_offsets));
|
||||
break;
|
||||
case opc_putstatic:
|
||||
case opc_putfield:
|
||||
Exprent valfield = stack.pop();
|
||||
Exprent exprfield =
|
||||
new FieldExprent(pool.getLinkConstant(instr.getOperand(0)), instr.opcode == opc_putstatic ? null : stack.pop(), bytecode_offsets);
|
||||
new FieldExprent(pool.getLinkConstant(instr.operand(0)), instr.opcode == opc_putstatic ? null : stack.pop(),
|
||||
bytecode_offsets);
|
||||
exprlist.add(new AssignmentExprent(exprfield, valfield, bytecode_offsets));
|
||||
break;
|
||||
case opc_invokevirtual:
|
||||
@@ -566,19 +512,15 @@ public class ExprProcessor implements CodeConstants {
|
||||
case opc_invokestatic:
|
||||
case opc_invokeinterface:
|
||||
case opc_invokedynamic:
|
||||
if (instr.opcode != opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) {
|
||||
|
||||
LinkConstant invoke_constant = pool.getLinkConstant(instr.getOperand(0));
|
||||
int dynamic_invokation_type = -1;
|
||||
if (instr.opcode != opc_invokedynamic || instr.bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) {
|
||||
LinkConstant invoke_constant = pool.getLinkConstant(instr.operand(0));
|
||||
|
||||
List<PooledConstant> bootstrap_arguments = null;
|
||||
if (instr.opcode == opc_invokedynamic && bootstrap != null) {
|
||||
List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.index1);
|
||||
LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1);
|
||||
|
||||
dynamic_invokation_type = content_method_handle.index1;
|
||||
bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.index1);
|
||||
}
|
||||
|
||||
InvocationExprent exprinv = new InvocationExprent(instr.opcode, invoke_constant, stack, dynamic_invokation_type, bytecode_offsets);
|
||||
InvocationExprent exprinv = new InvocationExprent(instr.opcode, invoke_constant, bootstrap_arguments, stack, bytecode_offsets);
|
||||
if (exprinv.getDescriptor().ret.type == CodeConstants.TYPE_VOID) {
|
||||
exprlist.add(exprinv);
|
||||
}
|
||||
@@ -590,15 +532,15 @@ public class ExprProcessor implements CodeConstants {
|
||||
case opc_new:
|
||||
case opc_anewarray:
|
||||
case opc_multianewarray:
|
||||
int dimensions = (instr.opcode == opc_new) ? 0 : (instr.opcode == opc_anewarray) ? 1 : instr.getOperand(1);
|
||||
VarType arrType = new VarType(pool.getPrimitiveConstant(instr.getOperand(0)).getString(), true);
|
||||
int dimensions = (instr.opcode == opc_new) ? 0 : (instr.opcode == opc_anewarray) ? 1 : instr.operand(1);
|
||||
VarType arrType = new VarType(pool.getPrimitiveConstant(instr.operand(0)).getString(), true);
|
||||
if (instr.opcode != opc_multianewarray) {
|
||||
arrType = arrType.resizeArrayDim(arrType.arrayDim + dimensions);
|
||||
}
|
||||
pushEx(stack, exprlist, new NewExprent(arrType, stack, dimensions, bytecode_offsets));
|
||||
break;
|
||||
case opc_newarray:
|
||||
pushEx(stack, exprlist, new NewExprent(new VarType(arr_type[instr.getOperand(0) - 4], 1), stack, 1, bytecode_offsets));
|
||||
pushEx(stack, exprlist, new NewExprent(new VarType(arrTypeIds[instr.operand(0) - 4], 1), stack, 1, bytecode_offsets));
|
||||
break;
|
||||
case opc_dup:
|
||||
pushEx(stack, exprlist, stack.getByOffset(-1).copy());
|
||||
@@ -657,12 +599,38 @@ public class ExprProcessor implements CodeConstants {
|
||||
stack.pop();
|
||||
break;
|
||||
case opc_pop:
|
||||
case opc_pop2:
|
||||
stack.pop();
|
||||
break;
|
||||
case opc_pop2:
|
||||
if (stack.getByOffset(-1).getExprType().stackSize == 1) {
|
||||
// Since value at the top of the stack is a value of category 1 (JVMS9 2.11.1)
|
||||
// we should remove one more item from the stack.
|
||||
// See JVMS9 pop2 chapter.
|
||||
stack.pop();
|
||||
}
|
||||
stack.pop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int nextMeaningfulOffset(BasicBlock block, int index) {
|
||||
InstructionSequence seq = block.getSeq();
|
||||
while (++index < seq.length()) {
|
||||
switch (seq.getInstr(index).opcode) {
|
||||
case opc_nop:
|
||||
case opc_istore:
|
||||
case opc_lstore:
|
||||
case opc_fstore:
|
||||
case opc_dstore:
|
||||
case opc_astore:
|
||||
continue;
|
||||
}
|
||||
return block.getOldOffset(index);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void pushEx(ExprentStack stack, List<Exprent> exprlist, Exprent exprent) {
|
||||
pushEx(stack, exprlist, exprent, null);
|
||||
}
|
||||
@@ -680,7 +648,7 @@ public class ExprProcessor implements CodeConstants {
|
||||
|
||||
int base = VarExprent.STACK_BASE + stack.size();
|
||||
|
||||
LinkedList<VarExprent> lst = new LinkedList<VarExprent>();
|
||||
LinkedList<VarExprent> lst = new LinkedList<>();
|
||||
|
||||
for (int i = -1; i >= offset; i--) {
|
||||
Exprent varex = stack.pop();
|
||||
@@ -706,7 +674,6 @@ public class ExprProcessor implements CodeConstants {
|
||||
}
|
||||
|
||||
public static String getTypeName(VarType type, boolean getShort) {
|
||||
|
||||
int tp = type.type;
|
||||
if (tp <= CodeConstants.TYPE_BOOLEAN) {
|
||||
return typeNames[tp];
|
||||
@@ -741,12 +708,9 @@ public class ExprProcessor implements CodeConstants {
|
||||
}
|
||||
|
||||
public static String getCastTypeName(VarType type, boolean getShort) {
|
||||
String s = getTypeName(type, getShort);
|
||||
int dim = type.arrayDim;
|
||||
while (dim-- > 0) {
|
||||
s += "[]";
|
||||
}
|
||||
return s;
|
||||
StringBuilder s = new StringBuilder(getTypeName(type, getShort));
|
||||
TextUtil.append(s, "[]", type.arrayDim);
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
public static PrimitiveExprsList getExpressionData(VarExprent var) {
|
||||
@@ -759,20 +723,20 @@ public class ExprProcessor implements CodeConstants {
|
||||
return prlst;
|
||||
}
|
||||
|
||||
public static boolean endsWithSemikolon(Exprent expr) {
|
||||
public static boolean endsWithSemicolon(Exprent expr) {
|
||||
int type = expr.type;
|
||||
return !(type == Exprent.EXPRENT_SWITCH ||
|
||||
type == Exprent.EXPRENT_MONITOR ||
|
||||
type == Exprent.EXPRENT_IF ||
|
||||
(type == Exprent.EXPRENT_VAR && ((VarExprent)expr)
|
||||
.isClassDef()));
|
||||
(type == Exprent.EXPRENT_VAR && ((VarExprent)expr).isClassDef()));
|
||||
}
|
||||
|
||||
private static void addDeletedGotoInstructionMapping(Statement stat, BytecodeMappingTracer tracer) {
|
||||
if (stat instanceof BasicBlockStatement) {
|
||||
BasicBlock block = ((BasicBlockStatement)stat).getBlock();
|
||||
List<Integer> offsets = block.getInstrOldOffsets();
|
||||
if (!offsets.isEmpty() && offsets.size() > block.getSeq().length()) { // some instructions have been deleted, but we still have offsets
|
||||
if (!offsets.isEmpty() &&
|
||||
offsets.size() > block.getSeq().length()) { // some instructions have been deleted, but we still have offsets
|
||||
tracer.addMapping(offsets.get(offsets.size() - 1)); // add the last offset
|
||||
}
|
||||
}
|
||||
@@ -827,7 +791,7 @@ public class ExprProcessor implements CodeConstants {
|
||||
return res;
|
||||
}
|
||||
|
||||
public static TextBuffer listToJava(List<Exprent> lst, int indent, BytecodeMappingTracer tracer) {
|
||||
public static TextBuffer listToJava(List<? extends Exprent> lst, int indent, BytecodeMappingTracer tracer) {
|
||||
if (lst == null || lst.isEmpty()) {
|
||||
return new TextBuffer();
|
||||
}
|
||||
@@ -835,7 +799,14 @@ public class ExprProcessor implements CodeConstants {
|
||||
TextBuffer buf = new TextBuffer();
|
||||
|
||||
for (Exprent expr : lst) {
|
||||
if (buf.length() > 0 && expr.type == Exprent.EXPRENT_VAR && ((VarExprent)expr).isClassDef()) {
|
||||
// separates local class definition from previous statements
|
||||
buf.appendLineSeparator();
|
||||
tracer.incrementCurrentSourceLine();
|
||||
}
|
||||
|
||||
TextBuffer content = expr.toJava(indent, tracer);
|
||||
|
||||
if (content.length() > 0) {
|
||||
if (expr.type != Exprent.EXPRENT_VAR || !((VarExprent)expr).isClassDef()) {
|
||||
buf.appendIndent(indent);
|
||||
@@ -844,7 +815,7 @@ public class ExprProcessor implements CodeConstants {
|
||||
if (expr.type == Exprent.EXPRENT_MONITOR && ((MonitorExprent)expr).getMonType() == MonitorExprent.MONITOR_ENTER) {
|
||||
buf.append("{}"); // empty synchronized block
|
||||
}
|
||||
if (endsWithSemikolon(expr)) {
|
||||
if (endsWithSemicolon(expr)) {
|
||||
buf.append(";");
|
||||
}
|
||||
buf.appendLineSeparator();
|
||||
@@ -855,26 +826,24 @@ public class ExprProcessor implements CodeConstants {
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static ConstExprent getDefaultArrayValue(VarType arrtype) {
|
||||
|
||||
ConstExprent defaultval;
|
||||
if (arrtype.type == CodeConstants.TYPE_OBJECT || arrtype.arrayDim > 0) {
|
||||
defaultval = new ConstExprent(VarType.VARTYPE_NULL, null, null);
|
||||
public static ConstExprent getDefaultArrayValue(VarType arrType) {
|
||||
ConstExprent defaultVal;
|
||||
if (arrType.type == CodeConstants.TYPE_OBJECT || arrType.arrayDim > 0) {
|
||||
defaultVal = new ConstExprent(VarType.VARTYPE_NULL, null, null);
|
||||
}
|
||||
else if (arrtype.type == CodeConstants.TYPE_FLOAT) {
|
||||
defaultval = new ConstExprent(VarType.VARTYPE_FLOAT, new Float(0), null);
|
||||
else if (arrType.type == CodeConstants.TYPE_FLOAT) {
|
||||
defaultVal = new ConstExprent(VarType.VARTYPE_FLOAT, 0f, null);
|
||||
}
|
||||
else if (arrtype.type == CodeConstants.TYPE_LONG) {
|
||||
defaultval = new ConstExprent(VarType.VARTYPE_LONG, new Long(0), null);
|
||||
else if (arrType.type == CodeConstants.TYPE_LONG) {
|
||||
defaultVal = new ConstExprent(VarType.VARTYPE_LONG, 0L, null);
|
||||
}
|
||||
else if (arrtype.type == CodeConstants.TYPE_DOUBLE) {
|
||||
defaultval = new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(0), null);
|
||||
else if (arrType.type == CodeConstants.TYPE_DOUBLE) {
|
||||
defaultVal = new ConstExprent(VarType.VARTYPE_DOUBLE, 0d, null);
|
||||
}
|
||||
else { // integer types
|
||||
defaultval = new ConstExprent(0, true, null);
|
||||
defaultVal = new ConstExprent(0, true, null);
|
||||
}
|
||||
|
||||
return defaultval;
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
public static boolean getCastedExprent(Exprent exprent,
|
||||
@@ -883,7 +852,7 @@ public class ExprProcessor implements CodeConstants {
|
||||
int indent,
|
||||
boolean castNull,
|
||||
BytecodeMappingTracer tracer) {
|
||||
return getCastedExprent(exprent, leftType, buffer, indent, castNull, false, tracer);
|
||||
return getCastedExprent(exprent, leftType, buffer, indent, castNull, false, false, false, tracer);
|
||||
}
|
||||
|
||||
public static boolean getCastedExprent(Exprent exprent,
|
||||
@@ -892,18 +861,42 @@ public class ExprProcessor implements CodeConstants {
|
||||
int indent,
|
||||
boolean castNull,
|
||||
boolean castAlways,
|
||||
boolean castNarrowing,
|
||||
boolean unbox,
|
||||
BytecodeMappingTracer tracer) {
|
||||
|
||||
if (unbox) {
|
||||
// "unbox" invocation parameters, e.g. 'byteSet.add((byte)123)' or 'new ShortContainer((short)813)'
|
||||
if (exprent.type == Exprent.EXPRENT_INVOCATION && ((InvocationExprent)exprent).isBoxingCall()) {
|
||||
InvocationExprent invocationExprent = (InvocationExprent)exprent;
|
||||
exprent = invocationExprent.getLstParameters().get(0);
|
||||
int paramType = invocationExprent.getDescriptor().params[0].type;
|
||||
if (exprent.type == Exprent.EXPRENT_CONST && ((ConstExprent)exprent).getConstType().type != paramType) {
|
||||
leftType = new VarType(paramType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VarType rightType = exprent.getExprType();
|
||||
|
||||
boolean cast =
|
||||
castAlways ||
|
||||
(!leftType.isSuperset(rightType) && (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT)) ||
|
||||
(castNull && rightType.type == CodeConstants.TYPE_NULL && !UNDEFINED_TYPE_STRING.equals(getTypeName(leftType))) ||
|
||||
(isIntConstant(exprent) && VarType.VARTYPE_INT.isStrictSuperset(leftType));
|
||||
|
||||
(castNarrowing && isIntConstant(exprent) && isNarrowedIntType(leftType));
|
||||
|
||||
boolean quote = cast && exprent.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST);
|
||||
|
||||
// cast instead to 'byte' / 'short' when int constant is used as a value for 'Byte' / 'Short'
|
||||
if (castNarrowing && exprent.type == Exprent.EXPRENT_CONST && !((ConstExprent) exprent).isNull()) {
|
||||
if (leftType.equals(VarType.VARTYPE_BYTE_OBJ)) {
|
||||
leftType = VarType.VARTYPE_BYTE;
|
||||
}
|
||||
else if (leftType.equals(VarType.VARTYPE_SHORT_OBJ)) {
|
||||
leftType = VarType.VARTYPE_SHORT;
|
||||
}
|
||||
}
|
||||
|
||||
if (cast) buffer.append('(').append(getCastTypeName(leftType)).append(')');
|
||||
|
||||
if (quote) buffer.append('(');
|
||||
@@ -920,10 +913,8 @@ public class ExprProcessor implements CodeConstants {
|
||||
}
|
||||
|
||||
private static boolean isIntConstant(Exprent exprent) {
|
||||
|
||||
if (exprent.type == Exprent.EXPRENT_CONST) {
|
||||
ConstExprent cexpr = (ConstExprent)exprent;
|
||||
switch (cexpr.getConstType().type) {
|
||||
switch (((ConstExprent)exprent).getConstType().type) {
|
||||
case CodeConstants.TYPE_BYTE:
|
||||
case CodeConstants.TYPE_BYTECHAR:
|
||||
case CodeConstants.TYPE_SHORT:
|
||||
@@ -935,4 +926,9 @@ public class ExprProcessor implements CodeConstants {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isNarrowedIntType(VarType type) {
|
||||
return VarType.VARTYPE_INT.isStrictSuperset(type) ||
|
||||
type.equals(VarType.VARTYPE_BYTE_OBJ) || type.equals(VarType.VARTYPE_SHORT_OBJ);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.modules.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
|
||||
@@ -28,17 +14,13 @@ public class ExprentStack extends ListStack<Exprent> {
|
||||
pointer = list.getPointer();
|
||||
}
|
||||
|
||||
public Exprent push(Exprent item) {
|
||||
super.push(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exprent pop() {
|
||||
|
||||
return this.remove(--pointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExprentStack clone() {
|
||||
return new ExprentStack(this);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.modules.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.*;
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
import org.jetbrains.java.decompiler.code.Instruction;
|
||||
import org.jetbrains.java.decompiler.code.InstructionSequence;
|
||||
import org.jetbrains.java.decompiler.code.SimpleInstructionSequence;
|
||||
import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
|
||||
import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
|
||||
import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;
|
||||
@@ -38,6 +27,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
|
||||
import org.jetbrains.java.decompiler.struct.StructMethod;
|
||||
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
|
||||
@@ -45,30 +35,28 @@ import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class FinallyProcessor {
|
||||
private final Map<Integer, Integer> finallyBlockIDs = new HashMap<>();
|
||||
private final Map<Integer, Integer> catchallBlockIDs = new HashMap<>();
|
||||
|
||||
private final Map<Integer, Integer> finallyBlockIDs = new HashMap<Integer, Integer>();
|
||||
private final Map<Integer, Integer> catchallBlockIDs = new HashMap<Integer, Integer>();
|
||||
private final MethodDescriptor methodDescriptor;
|
||||
private final VarProcessor varProcessor;
|
||||
|
||||
private final VarProcessor varprocessor;
|
||||
|
||||
public FinallyProcessor(VarProcessor varprocessor) {
|
||||
this.varprocessor = varprocessor;
|
||||
public FinallyProcessor(MethodDescriptor md, VarProcessor varProc) {
|
||||
methodDescriptor = md;
|
||||
varProcessor = varProc;
|
||||
}
|
||||
|
||||
public boolean iterateGraph(StructMethod mt, RootStatement root, ControlFlowGraph graph) {
|
||||
// return processStatement(mt, root, graph, root);
|
||||
return processStatementEx(mt, root, graph);
|
||||
}
|
||||
|
||||
private boolean processStatementEx(StructMethod mt, RootStatement root, ControlFlowGraph graph) {
|
||||
|
||||
int bytecode_version = mt.getClassStruct().getBytecodeVersion();
|
||||
|
||||
LinkedList<Statement> stack = new LinkedList<Statement>();
|
||||
LinkedList<Statement> stack = new LinkedList<>();
|
||||
stack.add(root);
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
|
||||
Statement stat = stack.removeLast();
|
||||
|
||||
Statement parent = stat.getParent();
|
||||
@@ -83,14 +71,12 @@ public class FinallyProcessor {
|
||||
// do nothing
|
||||
}
|
||||
else if (finallyBlockIDs.containsKey(handler.id)) {
|
||||
|
||||
fin.setFinally(true);
|
||||
|
||||
Integer var = finallyBlockIDs.get(handler.id);
|
||||
fin.setMonitor(var == null ? null : new VarExprent(var.intValue(), VarType.VARTYPE_INT, varprocessor));
|
||||
fin.setMonitor(var == null ? null : new VarExprent(var, VarType.VARTYPE_INT, varProcessor));
|
||||
}
|
||||
else {
|
||||
|
||||
Record inf = getFinallyInformation(mt, root, fin);
|
||||
|
||||
if (inf == null) { // inconsistent finally
|
||||
@@ -124,62 +110,6 @@ public class FinallyProcessor {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// private boolean processStatement(StructMethod mt, RootStatement root, ControlFlowGraph graph, Statement stat) {
|
||||
//
|
||||
// boolean res = false;
|
||||
//
|
||||
// for(int i=stat.getStats().size()-1;i>=0;i--) {
|
||||
// if(processStatement(mt, root, graph, stat.getStats().get(i))) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if(stat.type == Statement.TYPE_CATCHALL && !stat.isCopied()) {
|
||||
//
|
||||
// CatchAllStatement fin = (CatchAllStatement)stat;
|
||||
// BasicBlock head = fin.getBasichead().getBlock();
|
||||
// BasicBlock handler = fin.getHandler().getBasichead().getBlock();
|
||||
//
|
||||
// if(catchallBlockIDs.containsKey(handler.id)) {
|
||||
// ; // do nothing
|
||||
// }else if(finallyBlockIDs.containsKey(handler.id)) {
|
||||
//
|
||||
// fin.setFinally(true);
|
||||
//
|
||||
// Integer var = finallyBlockIDs.get(handler.id);
|
||||
// fin.setMonitor(var==null?null:new VarExprent(var.intValue(), VarType.VARTYPE_INT, varprocessor));
|
||||
//
|
||||
// } else {
|
||||
//
|
||||
// Object[] inf = getFinallyInformation(mt, root, fin);
|
||||
//
|
||||
// if(inf == null) { // inconsistent finally
|
||||
// catchallBlockIDs.put(handler.id, null);
|
||||
// } else {
|
||||
//
|
||||
// if(DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) {
|
||||
// finallyBlockIDs.put(handler.id, null);
|
||||
// } else {
|
||||
//
|
||||
// int varindex = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER);
|
||||
// insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf);
|
||||
//
|
||||
// finallyBlockIDs.put(handler.id, varindex);
|
||||
// }
|
||||
//
|
||||
// DeadCodeHelper.removeEmptyBlocks(graph);
|
||||
// DeadCodeHelper.mergeBasicBlocks(graph);
|
||||
// }
|
||||
//
|
||||
// res = true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return res;
|
||||
// }
|
||||
|
||||
private static class Record {
|
||||
private final int firstCode;
|
||||
private final Map<BasicBlock, Boolean> mapLast;
|
||||
@@ -190,10 +120,8 @@ public class FinallyProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Record getFinallyInformation(StructMethod mt, RootStatement root, CatchAllStatement fstat) {
|
||||
|
||||
Map<BasicBlock, Boolean> mapLast = new HashMap<BasicBlock, Boolean>();
|
||||
private Record getFinallyInformation(StructMethod mt, RootStatement root, CatchAllStatement fstat) {
|
||||
Map<BasicBlock, Boolean> mapLast = new HashMap<>();
|
||||
|
||||
BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead();
|
||||
BasicBlock firstBasicBlock = firstBlockStatement.getBlock();
|
||||
@@ -209,7 +137,7 @@ public class FinallyProcessor {
|
||||
firstcode = 2;
|
||||
}
|
||||
|
||||
ExprProcessor proc = new ExprProcessor();
|
||||
ExprProcessor proc = new ExprProcessor(methodDescriptor, varProcessor);
|
||||
proc.processStatement(root, mt.getClassStruct());
|
||||
|
||||
SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
|
||||
@@ -222,13 +150,12 @@ public class FinallyProcessor {
|
||||
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
|
||||
DirectGraph dgraph = flatthelper.buildDirectGraph(root);
|
||||
|
||||
LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
|
||||
LinkedList<DirectNode> stack = new LinkedList<>();
|
||||
stack.add(dgraph.first);
|
||||
|
||||
Set<DirectNode> setVisited = new HashSet<DirectNode>();
|
||||
Set<DirectNode> setVisited = new HashSet<>();
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
|
||||
DirectNode node = stack.removeFirst();
|
||||
|
||||
if (setVisited.contains(node)) {
|
||||
@@ -375,8 +302,7 @@ public class FinallyProcessor {
|
||||
int var,
|
||||
Record information,
|
||||
int bytecode_version) {
|
||||
|
||||
Set<BasicBlock> setCopy = new HashSet<BasicBlock>(setTry);
|
||||
Set<BasicBlock> setCopy = new HashSet<>(setTry);
|
||||
|
||||
int finallytype = information.firstCode;
|
||||
Map<BasicBlock, Boolean> mapLast = information.mapLast;
|
||||
@@ -394,21 +320,15 @@ public class FinallyProcessor {
|
||||
|
||||
// disable semaphore at statement exit points
|
||||
for (BasicBlock block : setTry) {
|
||||
|
||||
List<BasicBlock> lstSucc = block.getSuccs();
|
||||
for (BasicBlock dest : lstSucc) {
|
||||
|
||||
for (BasicBlock dest : lstSucc) {
|
||||
// break out
|
||||
if (!setCopy.contains(dest) && dest != graph.getLast()) {
|
||||
if (dest != graph.getLast() && !setCopy.contains(dest)) {
|
||||
// disable semaphore
|
||||
SimpleInstructionSequence seq = new SimpleInstructionSequence();
|
||||
|
||||
seq.addInstruction(ConstantsUtil
|
||||
.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version,
|
||||
new int[]{0}), -1);
|
||||
seq.addInstruction(ConstantsUtil
|
||||
.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version,
|
||||
new int[]{var}), -1);
|
||||
seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), -1);
|
||||
seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), -1);
|
||||
|
||||
// build a separate block
|
||||
BasicBlock newblock = new BasicBlock(++graph.last_id);
|
||||
@@ -435,14 +355,10 @@ public class FinallyProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
// enable semaphor at the statement entrance
|
||||
// enable semaphore at the statement entrance
|
||||
SimpleInstructionSequence seq = new SimpleInstructionSequence();
|
||||
seq.addInstruction(
|
||||
ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}),
|
||||
-1);
|
||||
seq.addInstruction(
|
||||
ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}),
|
||||
-1);
|
||||
seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}), -1);
|
||||
seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), -1);
|
||||
|
||||
BasicBlock newhead = new BasicBlock(++graph.last_id);
|
||||
newhead.setSeq(seq);
|
||||
@@ -451,12 +367,8 @@ public class FinallyProcessor {
|
||||
|
||||
// initialize semaphor with false
|
||||
seq = new SimpleInstructionSequence();
|
||||
seq.addInstruction(
|
||||
ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}),
|
||||
-1);
|
||||
seq.addInstruction(
|
||||
ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}),
|
||||
-1);
|
||||
seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), -1);
|
||||
seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), -1);
|
||||
|
||||
BasicBlock newheadinit = new BasicBlock(++graph.last_id);
|
||||
newheadinit.setSeq(seq);
|
||||
@@ -466,7 +378,7 @@ public class FinallyProcessor {
|
||||
setCopy.add(newhead);
|
||||
setCopy.add(newheadinit);
|
||||
|
||||
for (BasicBlock hd : new HashSet<BasicBlock>(newheadinit.getSuccExceptions())) {
|
||||
for (BasicBlock hd : new HashSet<>(newheadinit.getSuccExceptions())) {
|
||||
ExceptionRangeCFG range = graph.getExceptionRange(hd, newheadinit);
|
||||
|
||||
if (setCopy.containsAll(range.getProtectedRange())) {
|
||||
@@ -476,10 +388,8 @@ public class FinallyProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void insertBlockBefore(ControlFlowGraph graph, BasicBlock oldblock, BasicBlock newblock) {
|
||||
|
||||
List<BasicBlock> lstTemp = new ArrayList<BasicBlock>();
|
||||
List<BasicBlock> lstTemp = new ArrayList<>();
|
||||
lstTemp.addAll(oldblock.getPreds());
|
||||
lstTemp.addAll(oldblock.getPredExceptions());
|
||||
|
||||
@@ -510,9 +420,8 @@ public class FinallyProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<BasicBlock> getAllBasicBlocks(Statement stat) {
|
||||
|
||||
List<Statement> lst = new LinkedList<Statement>();
|
||||
private static Set<BasicBlock> getAllBasicBlocks(Statement stat) {
|
||||
List<Statement> lst = new LinkedList<>();
|
||||
lst.add(stat);
|
||||
|
||||
int index = 0;
|
||||
@@ -529,7 +438,7 @@ public class FinallyProcessor {
|
||||
}
|
||||
while (index < lst.size());
|
||||
|
||||
HashSet<BasicBlock> res = new HashSet<BasicBlock>();
|
||||
Set<BasicBlock> res = new HashSet<>();
|
||||
|
||||
for (Statement st : lst) {
|
||||
res.add(((BasicBlockStatement)st).getBlock());
|
||||
@@ -538,11 +447,9 @@ public class FinallyProcessor {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
private boolean verifyFinallyEx(ControlFlowGraph graph, CatchAllStatement fstat, Record information) {
|
||||
|
||||
HashSet<BasicBlock> tryBlocks = getAllBasicBlocks(fstat.getFirst());
|
||||
HashSet<BasicBlock> catchBlocks = getAllBasicBlocks(fstat.getHandler());
|
||||
Set<BasicBlock> tryBlocks = getAllBasicBlocks(fstat.getFirst());
|
||||
Set<BasicBlock> catchBlocks = getAllBasicBlocks(fstat.getHandler());
|
||||
|
||||
int finallytype = information.firstCode;
|
||||
Map<BasicBlock, Boolean> mapLast = information.mapLast;
|
||||
@@ -571,7 +478,7 @@ public class FinallyProcessor {
|
||||
}
|
||||
|
||||
// identify start blocks
|
||||
HashSet<BasicBlock> startBlocks = new HashSet<BasicBlock>();
|
||||
Set<BasicBlock> startBlocks = new HashSet<>();
|
||||
for (BasicBlock block : tryBlocks) {
|
||||
startBlocks.addAll(block.getSuccs());
|
||||
}
|
||||
@@ -580,7 +487,7 @@ public class FinallyProcessor {
|
||||
startBlocks.remove(graph.getLast());
|
||||
startBlocks.removeAll(tryBlocks);
|
||||
|
||||
List<Area> lstAreas = new ArrayList<Area>();
|
||||
List<Area> lstAreas = new ArrayList<>();
|
||||
|
||||
for (BasicBlock start : startBlocks) {
|
||||
|
||||
@@ -634,33 +541,32 @@ public class FinallyProcessor {
|
||||
|
||||
private Area compareSubgraphsEx(ControlFlowGraph graph,
|
||||
BasicBlock startSample,
|
||||
HashSet<BasicBlock> catchBlocks,
|
||||
Set<BasicBlock> catchBlocks,
|
||||
BasicBlock startCatch,
|
||||
int finallytype,
|
||||
Map<BasicBlock, Boolean> mapLast,
|
||||
boolean skippedFirst) {
|
||||
|
||||
class BlockStackEntry {
|
||||
public BasicBlock blockCatch;
|
||||
public BasicBlock blockSample;
|
||||
public final BasicBlock blockCatch;
|
||||
public final BasicBlock blockSample;
|
||||
|
||||
// TODO: correct handling (merging) of multiple paths
|
||||
public List<int[]> lstStoreVars;
|
||||
public final List<int[]> lstStoreVars;
|
||||
|
||||
public BlockStackEntry(BasicBlock blockCatch, BasicBlock blockSample, List<int[]> lstStoreVars) {
|
||||
BlockStackEntry(BasicBlock blockCatch, BasicBlock blockSample, List<int[]> lstStoreVars) {
|
||||
this.blockCatch = blockCatch;
|
||||
this.blockSample = blockSample;
|
||||
this.lstStoreVars = new ArrayList<int[]>(lstStoreVars);
|
||||
this.lstStoreVars = new ArrayList<>(lstStoreVars);
|
||||
}
|
||||
}
|
||||
|
||||
List<BlockStackEntry> stack = new LinkedList<BlockStackEntry>();
|
||||
List<BlockStackEntry> stack = new LinkedList<>();
|
||||
|
||||
Set<BasicBlock> setSample = new HashSet<BasicBlock>();
|
||||
Set<BasicBlock> setSample = new HashSet<>();
|
||||
|
||||
Map<String, BasicBlock[]> mapNext = new HashMap<String, BasicBlock[]>();
|
||||
Map<String, BasicBlock[]> mapNext = new HashMap<>();
|
||||
|
||||
stack.add(new BlockStackEntry(startCatch, startSample, new ArrayList<int[]>()));
|
||||
stack.add(new BlockStackEntry(startCatch, startSample, new ArrayList<>()));
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
|
||||
@@ -693,7 +599,6 @@ public class FinallyProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// exception successors
|
||||
if (isLastBlock && blockSample.getSeq().isEmpty()) {
|
||||
// do nothing, blockSample will be removed anyway
|
||||
@@ -721,8 +626,8 @@ public class FinallyProcessor {
|
||||
|
||||
if (instrCatch.opcode == CodeConstants.opc_astore &&
|
||||
instrSample.opcode == CodeConstants.opc_astore) {
|
||||
lst = new ArrayList<int[]>(lst);
|
||||
lst.add(new int[]{instrCatch.getOperand(0), instrSample.getOperand(0)});
|
||||
lst = new ArrayList<>(lst);
|
||||
lst.add(new int[]{instrCatch.operand(0), instrSample.operand(0)});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -740,7 +645,7 @@ public class FinallyProcessor {
|
||||
}
|
||||
|
||||
if (isLastBlock) {
|
||||
Set<BasicBlock> setSuccs = new HashSet<BasicBlock>(blockSample.getSuccs());
|
||||
Set<BasicBlock> setSuccs = new HashSet<>(blockSample.getSuccs());
|
||||
setSuccs.removeAll(setSample);
|
||||
|
||||
for (BlockStackEntry stackent : stack) {
|
||||
@@ -755,11 +660,10 @@ public class FinallyProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
return new Area(startSample, setSample, getUniqueNext(graph, new HashSet<BasicBlock[]>(mapNext.values())));
|
||||
return new Area(startSample, setSample, getUniqueNext(graph, new HashSet<>(mapNext.values())));
|
||||
}
|
||||
|
||||
private static BasicBlock getUniqueNext(ControlFlowGraph graph, Set<BasicBlock[]> setNext) {
|
||||
|
||||
// precondition: there is at most one true exit path in a finally statement
|
||||
|
||||
BasicBlock next = null;
|
||||
@@ -800,13 +704,11 @@ public class FinallyProcessor {
|
||||
Instruction instrNext = seqNext.getInstr(i);
|
||||
Instruction instrBlock = seqBlock.getInstr(i);
|
||||
|
||||
if (instrNext.opcode != instrBlock.opcode || instrNext.wide != instrBlock.wide
|
||||
|| instrNext.operandsCount() != instrBlock.operandsCount()) {
|
||||
if (!Instruction.equals(instrNext, instrBlock)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int j = 0; j < instrNext.getOperands().length; j++) {
|
||||
if (instrNext.getOperand(j) != instrBlock.getOperand(j)) {
|
||||
for (int j = 0; j < instrNext.operandsCount(); j++) {
|
||||
if (instrNext.operand(j) != instrBlock.operand(j)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -848,7 +750,6 @@ public class FinallyProcessor {
|
||||
int type,
|
||||
int finallytype,
|
||||
List<int[]> lstStoreVars) {
|
||||
|
||||
InstructionSequence seqPattern = pattern.getSeq();
|
||||
InstructionSequence seqSample = sample.getSeq();
|
||||
|
||||
@@ -887,9 +788,8 @@ public class FinallyProcessor {
|
||||
}
|
||||
|
||||
if (seqPattern.length() < seqSample.length()) { // split in two blocks
|
||||
|
||||
SimpleInstructionSequence seq = new SimpleInstructionSequence();
|
||||
LinkedList<Integer> oldOffsets = new LinkedList<Integer>();
|
||||
LinkedList<Integer> oldOffsets = new LinkedList<>();
|
||||
for (int i = seqSample.length() - 1; i >= seqPattern.length(); i--) {
|
||||
seq.addInstruction(0, seqSample.getInstr(i), -1);
|
||||
oldOffsets.addFirst(sample.getOldOffset(i));
|
||||
@@ -900,8 +800,7 @@ public class FinallyProcessor {
|
||||
newblock.setSeq(seq);
|
||||
newblock.getInstrOldOffsets().addAll(oldOffsets);
|
||||
|
||||
List<BasicBlock> lstTemp = new ArrayList<BasicBlock>();
|
||||
lstTemp.addAll(sample.getSuccs());
|
||||
List<BasicBlock> lstTemp = new ArrayList<>(sample.getSuccs());
|
||||
|
||||
// move successors
|
||||
for (BasicBlock suc : lstTemp) {
|
||||
@@ -933,19 +832,15 @@ public class FinallyProcessor {
|
||||
}
|
||||
|
||||
public boolean equalInstructions(Instruction first, Instruction second, List<int[]> lstStoreVars) {
|
||||
if (first.opcode != second.opcode || first.wide != second.wide
|
||||
|| first.operandsCount() != second.operandsCount()) {
|
||||
if (!Instruction.equals(first, second)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (first.group != CodeConstants.GROUP_JUMP && first.getOperands() != null) { // FIXME: switch comparison
|
||||
for (int i = 0; i < first.getOperands().length; i++) {
|
||||
|
||||
int firstOp = first.getOperand(i);
|
||||
int secondOp = second.getOperand(i);
|
||||
|
||||
if (first.group != CodeConstants.GROUP_JUMP) { // FIXME: switch comparison
|
||||
for (int i = 0; i < first.operandsCount(); i++) {
|
||||
int firstOp = first.operand(i);
|
||||
int secondOp = second.operand(i);
|
||||
if (firstOp != secondOp) {
|
||||
|
||||
// a-load/store instructions
|
||||
if (first.opcode == CodeConstants.opc_aload || first.opcode == CodeConstants.opc_astore) {
|
||||
for (int[] arr : lstStoreVars) {
|
||||
@@ -964,7 +859,6 @@ public class FinallyProcessor {
|
||||
}
|
||||
|
||||
private static void deleteArea(ControlFlowGraph graph, Area area) {
|
||||
|
||||
BasicBlock start = area.start;
|
||||
BasicBlock next = area.next;
|
||||
|
||||
@@ -978,14 +872,14 @@ public class FinallyProcessor {
|
||||
}
|
||||
|
||||
// collect common exception ranges of predecessors and successors
|
||||
Set<BasicBlock> setCommonExceptionHandlers = new HashSet<BasicBlock>(next.getSuccExceptions());
|
||||
Set<BasicBlock> setCommonExceptionHandlers = new HashSet<>(next.getSuccExceptions());
|
||||
for (BasicBlock pred : start.getPreds()) {
|
||||
setCommonExceptionHandlers.retainAll(pred.getSuccExceptions());
|
||||
}
|
||||
|
||||
boolean is_outside_range = false;
|
||||
|
||||
Set<BasicBlock> setPredecessors = new HashSet<BasicBlock>(start.getPreds());
|
||||
Set<BasicBlock> setPredecessors = new HashSet<>(start.getPreds());
|
||||
|
||||
// replace start with next
|
||||
for (BasicBlock pred : setPredecessors) {
|
||||
@@ -1007,7 +901,7 @@ public class FinallyProcessor {
|
||||
is_outside_range = true;
|
||||
}
|
||||
|
||||
Set<ExceptionRangeCFG> setRemovedExceptionRanges = new HashSet<ExceptionRangeCFG>();
|
||||
Set<ExceptionRangeCFG> setRemovedExceptionRanges = new HashSet<>();
|
||||
for (BasicBlock handler : block.getSuccExceptions()) {
|
||||
setRemovedExceptionRanges.add(graph.getExceptionRange(handler, block));
|
||||
}
|
||||
@@ -1022,7 +916,7 @@ public class FinallyProcessor {
|
||||
// shift extern edges on splitted blocks
|
||||
if (block.getSeq().isEmpty() && block.getSuccs().size() == 1) {
|
||||
BasicBlock succs = block.getSuccs().get(0);
|
||||
for (BasicBlock pred : new ArrayList<BasicBlock>(block.getPreds())) {
|
||||
for (BasicBlock pred : new ArrayList<>(block.getPreds())) {
|
||||
if (!setBlocks.contains(pred)) {
|
||||
pred.replaceSuccessor(block, succs);
|
||||
}
|
||||
@@ -1038,18 +932,15 @@ public class FinallyProcessor {
|
||||
}
|
||||
|
||||
if (is_outside_range) {
|
||||
|
||||
// new empty block
|
||||
BasicBlock emptyblock = new BasicBlock(++graph.last_id);
|
||||
|
||||
graph.getBlocks().addWithKey(emptyblock, emptyblock.id);
|
||||
|
||||
// add to ranges if necessary
|
||||
if (setCommonRemovedExceptionRanges != null) {
|
||||
for (ExceptionRangeCFG range : setCommonRemovedExceptionRanges) {
|
||||
emptyblock.addSuccessorException(range.getHandler());
|
||||
range.getProtectedRange().add(emptyblock);
|
||||
}
|
||||
for (ExceptionRangeCFG range : setCommonRemovedExceptionRanges) {
|
||||
emptyblock.addSuccessorException(range.getHandler());
|
||||
range.getProtectedRange().add(emptyblock);
|
||||
}
|
||||
|
||||
// insert between predecessors and next
|
||||
@@ -1061,7 +952,6 @@ public class FinallyProcessor {
|
||||
}
|
||||
|
||||
private static void removeExceptionInstructionsEx(BasicBlock block, int blocktype, int finallytype) {
|
||||
|
||||
InstructionSequence seq = block.getSeq();
|
||||
|
||||
if (finallytype == 3) { // empty finally handler
|
||||
@@ -1087,4 +977,4 @@ public class FinallyProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.modules.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
@@ -25,7 +11,6 @@ import org.jetbrains.java.decompiler.struct.attr.StructAnnotationAttribute;
|
||||
import org.jetbrains.java.decompiler.struct.attr.StructAnnotationParameterAttribute;
|
||||
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
|
||||
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
|
||||
import org.jetbrains.java.decompiler.util.VBStyleCollection;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -85,11 +70,10 @@ public class IdeaNotNullHelper {
|
||||
boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);
|
||||
|
||||
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
|
||||
VBStyleCollection<StructGeneralAttribute, String> attributes = mt.getAttributes();
|
||||
|
||||
// parameter annotations
|
||||
StructAnnotationParameterAttribute param_annotations = (StructAnnotationParameterAttribute)attributes
|
||||
.getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS);
|
||||
StructAnnotationParameterAttribute param_annotations =
|
||||
mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS);
|
||||
if (param_annotations != null) {
|
||||
|
||||
List<List<AnnotationExprent>> param_annotations_lists = param_annotations.getParamAnnotations();
|
||||
@@ -109,6 +93,7 @@ public class IdeaNotNullHelper {
|
||||
for (AnnotationExprent ann : annotations) {
|
||||
if (ann.getClassName().equals("org/jetbrains/annotations/NotNull")) {
|
||||
is_notnull_check = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +112,7 @@ public class IdeaNotNullHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
removeParameterCheck(stat, mt);
|
||||
removeParameterCheck(stat);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -135,7 +120,7 @@ public class IdeaNotNullHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void removeParameterCheck(Statement stat, StructMethod mt) {
|
||||
private static void removeParameterCheck(Statement stat) {
|
||||
|
||||
Statement st = stat.getFirst();
|
||||
while (st.type == Statement.TYPE_SEQUENCE) {
|
||||
@@ -144,11 +129,7 @@ public class IdeaNotNullHelper {
|
||||
|
||||
IfStatement ifstat = (IfStatement)st;
|
||||
|
||||
if (ifstat.getElsestat() == null) { // if
|
||||
// TODO:
|
||||
}
|
||||
else { // if - else
|
||||
|
||||
if (ifstat.getElsestat() != null) { // if - else
|
||||
StatEdge ifedge = ifstat.getIfEdge();
|
||||
StatEdge elseedge = ifstat.getElseEdge();
|
||||
|
||||
@@ -172,28 +153,22 @@ public class IdeaNotNullHelper {
|
||||
|
||||
private static boolean findAndRemoveReturnCheck(Statement stat, StructMethod mt) {
|
||||
|
||||
VBStyleCollection<StructGeneralAttribute, String> attributes = mt.getAttributes();
|
||||
|
||||
boolean is_notnull_check = false;
|
||||
|
||||
// method annotation, refers to the return value
|
||||
StructAnnotationAttribute attr =
|
||||
(StructAnnotationAttribute)attributes.getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS);
|
||||
StructAnnotationAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS);
|
||||
if (attr != null) {
|
||||
List<AnnotationExprent> annotations = attr.getAnnotations();
|
||||
|
||||
for (AnnotationExprent ann : annotations) {
|
||||
if (ann.getClassName().equals("org/jetbrains/annotations/NotNull")) {
|
||||
is_notnull_check = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_notnull_check) {
|
||||
return removeReturnCheck(stat, mt);
|
||||
}
|
||||
|
||||
return false;
|
||||
return is_notnull_check && removeReturnCheck(stat, mt);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.java.decompiler.modules.decompiler;
|
||||
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
|
||||
@@ -23,39 +9,25 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class IfHelper {
|
||||
|
||||
|
||||
public static boolean mergeAllIfs(RootStatement root) {
|
||||
|
||||
boolean res = mergeAllIfsRec(root, new HashSet<Integer>());
|
||||
|
||||
boolean res = mergeAllIfsRec(root, new HashSet<>());
|
||||
if (res) {
|
||||
SequenceHelper.condenseSequences(root);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
private static boolean mergeAllIfsRec(Statement stat, HashSet<Integer> setReorderedIfs) {
|
||||
|
||||
private static boolean mergeAllIfsRec(Statement stat, Set<? super Integer> setReorderedIfs) {
|
||||
boolean res = false;
|
||||
|
||||
if (stat.getExprents() == null) {
|
||||
|
||||
while (true) {
|
||||
|
||||
boolean changed = false;
|
||||
|
||||
for (Statement st : stat.getStats()) {
|
||||
|
||||
res |= mergeAllIfsRec(st, setReorderedIfs);
|
||||
|
||||
// collapse composed if's
|
||||
@@ -75,9 +47,7 @@ public class IfHelper {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
public static boolean mergeIfs(Statement statement, HashSet<Integer> setReorderedIfs) {
|
||||
|
||||
public static boolean mergeIfs(Statement statement, Set<? super Integer> setReorderedIfs) {
|
||||
if (statement.type != Statement.TYPE_IF && statement.type != Statement.TYPE_SEQUENCE) {
|
||||
return false;
|
||||
}
|
||||
@@ -85,10 +55,9 @@ public class IfHelper {
|
||||
boolean res = false;
|
||||
|
||||
while (true) {
|
||||
|
||||
boolean updated = false;
|
||||
|
||||
List<Statement> lst = new ArrayList<Statement>();
|
||||
List<Statement> lst = new ArrayList<>();
|
||||
if (statement.type == Statement.TYPE_IF) {
|
||||
lst.add(statement);
|
||||
}
|
||||
@@ -99,7 +68,6 @@ public class IfHelper {
|
||||
boolean stsingle = (lst.size() == 1);
|
||||
|
||||
for (Statement stat : lst) {
|
||||
|
||||
if (stat.type == Statement.TYPE_IF) {
|
||||
IfNode rtnode = buildGraph((IfStatement)stat, stsingle);
|
||||
|
||||
@@ -112,7 +80,6 @@ public class IfHelper {
|
||||
}
|
||||
|
||||
if (!setReorderedIfs.contains(stat.id)) {
|
||||
|
||||
if (updated = collapseIfElse(rtnode)) {
|
||||
break;
|
||||
}
|
||||
@@ -133,14 +100,13 @@ public class IfHelper {
|
||||
break;
|
||||
}
|
||||
|
||||
res |= updated;
|
||||
res |= true;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static boolean collapseIfIf(IfNode rtnode) {
|
||||
|
||||
if (rtnode.edgetypes.get(0) == 0) {
|
||||
IfNode ifbranch = rtnode.succs.get(0);
|
||||
if (ifbranch.succs.size() == 2) {
|
||||
@@ -158,7 +124,7 @@ public class IfHelper {
|
||||
ifchild.removeSuccessor(ifchild.getAllSuccessorEdges().get(0));
|
||||
ifparent.getStats().removeWithKey(ifchild.id);
|
||||
|
||||
if (ifbranch.edgetypes.get(0).intValue() == 1) { // target null
|
||||
if (ifbranch.edgetypes.get(0) == 1) { // target null
|
||||
|
||||
ifparent.setIfstat(null);
|
||||
|
||||
@@ -196,7 +162,7 @@ public class IfHelper {
|
||||
// merge if conditions
|
||||
IfExprent statexpr = ifparent.getHeadexprent();
|
||||
|
||||
List<Exprent> lstOperands = new ArrayList<Exprent>();
|
||||
List<Exprent> lstOperands = new ArrayList<>();
|
||||
lstOperands.add(statexpr.getCondition());
|
||||
lstOperands.add(ifchild.getHeadexprent().getCondition());
|
||||
|
||||
@@ -213,11 +179,9 @@ public class IfHelper {
|
||||
}
|
||||
|
||||
private static boolean collapseIfElse(IfNode rtnode) {
|
||||
|
||||
if (rtnode.edgetypes.get(0).intValue() == 0) {
|
||||
if (rtnode.edgetypes.get(0) == 0) {
|
||||
IfNode ifbranch = rtnode.succs.get(0);
|
||||
if (ifbranch.succs.size() == 2) {
|
||||
|
||||
// if-else branch
|
||||
if (ifbranch.succs.get(0).value == rtnode.succs.get(1).value) {
|
||||
|
||||
@@ -230,8 +194,8 @@ public class IfHelper {
|
||||
ifchild.getFirst().removeSuccessor(ifchild.getIfEdge());
|
||||
ifparent.getStats().removeWithKey(ifchild.id);
|
||||
|
||||
if (ifbranch.edgetypes.get(1).intValue() == 1 &&
|
||||
ifbranch.edgetypes.get(0).intValue() == 1) { // target null
|
||||
if (ifbranch.edgetypes.get(1) == 1 &&
|
||||
ifbranch.edgetypes.get(0) == 1) { // target null
|
||||
|
||||
ifparent.setIfstat(null);
|
||||
|
||||
@@ -250,7 +214,7 @@ public class IfHelper {
|
||||
// merge if conditions
|
||||
IfExprent statexpr = ifparent.getHeadexprent();
|
||||
|
||||
List<Exprent> lstOperands = new ArrayList<Exprent>();
|
||||
List<Exprent> lstOperands = new ArrayList<>();
|
||||
lstOperands.add(statexpr.getCondition());
|
||||
lstOperands.add(new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, ifchild.getHeadexprent().getCondition(), null));
|
||||
statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands, null));
|
||||
@@ -265,10 +229,8 @@ public class IfHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static boolean collapseElse(IfNode rtnode) {
|
||||
|
||||
if (rtnode.edgetypes.get(1).intValue() == 0) {
|
||||
if (rtnode.edgetypes.get(1) == 0) {
|
||||
IfNode elsebranch = rtnode.succs.get(1);
|
||||
if (elsebranch.succs.size() == 2) {
|
||||
|
||||
@@ -305,7 +267,7 @@ public class IfHelper {
|
||||
// merge if conditions
|
||||
IfExprent statexpr = secondif.getHeadexprent();
|
||||
|
||||
List<Exprent> lstOperands = new ArrayList<Exprent>();
|
||||
List<Exprent> lstOperands = new ArrayList<>();
|
||||
lstOperands.add(firstif.getHeadexprent().getCondition());
|
||||
|
||||
if (path == 2) {
|
||||
@@ -328,9 +290,7 @@ public class IfHelper {
|
||||
}
|
||||
}
|
||||
else if (elsebranch.succs.size() == 1) {
|
||||
|
||||
if (elsebranch.succs.get(0).value == rtnode.succs.get(0).value) {
|
||||
|
||||
IfStatement firstif = (IfStatement)rtnode.value;
|
||||
Statement second = elsebranch.value;
|
||||
|
||||
@@ -369,9 +329,7 @@ public class IfHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static IfNode buildGraph(IfStatement stat, boolean stsingle) {
|
||||
|
||||
if (stat.iftype == IfStatement.IFTYPE_IFELSE) {
|
||||
return null;
|
||||
}
|
||||
@@ -433,16 +391,14 @@ public class IfHelper {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// FIXME: rewrite the entire method!!! keep in mind finally exits!!
|
||||
private static boolean reorderIf(IfStatement ifstat) {
|
||||
|
||||
if (ifstat.iftype == IfStatement.IFTYPE_IFELSE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean ifdirect = false, elsedirect = false;
|
||||
boolean noifstat = false, noelsestat = false;
|
||||
boolean ifdirect, elsedirect;
|
||||
boolean noifstat = false, noelsestat;
|
||||
boolean ifdirectpath = false, elsedirectpath = false;
|
||||
|
||||
Statement parent = ifstat.getParent();
|
||||
@@ -453,32 +409,20 @@ public class IfHelper {
|
||||
if (ifstat.getIfstat() == null) {
|
||||
noifstat = true;
|
||||
|
||||
if (ifstat.getIfEdge().getType() == StatEdge.TYPE_FINALLYEXIT) {
|
||||
ifdirect = true;
|
||||
}
|
||||
else {
|
||||
ifdirect = MergeHelper.isDirectPath(from, ifstat.getIfEdge().getDestination());
|
||||
}
|
||||
ifdirect = ifstat.getIfEdge().getType() == StatEdge.TYPE_FINALLYEXIT ||
|
||||
MergeHelper.isDirectPath(from, ifstat.getIfEdge().getDestination());
|
||||
}
|
||||
else {
|
||||
List<StatEdge> lstSuccs = ifstat.getIfstat().getAllSuccessorEdges();
|
||||
if (!lstSuccs.isEmpty() && lstSuccs.get(0).getType() == StatEdge.TYPE_FINALLYEXIT) {
|
||||
ifdirect = true;
|
||||
}
|
||||
else {
|
||||
ifdirect = hasDirectEndEdge(ifstat.getIfstat(), from);
|
||||
}
|
||||
ifdirect = !lstSuccs.isEmpty() && lstSuccs.get(0).getType() == StatEdge.TYPE_FINALLYEXIT ||
|
||||
hasDirectEndEdge(ifstat.getIfstat(), from);
|
||||
}
|
||||
|
||||
Statement last = parent.type == Statement.TYPE_SEQUENCE ? parent.getStats().getLast() : ifstat;
|
||||
noelsestat = (last == ifstat);
|
||||
|
||||
if (!last.getAllSuccessorEdges().isEmpty() && last.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_FINALLYEXIT) {
|
||||
elsedirect = true;
|
||||
}
|
||||
else {
|
||||
elsedirect = hasDirectEndEdge(last, from);
|
||||
}
|
||||
elsedirect = !last.getAllSuccessorEdges().isEmpty() && last.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_FINALLYEXIT ||
|
||||
hasDirectEndEdge(last, from);
|
||||
|
||||
if (!noelsestat && existsPath(ifstat, ifstat.getAllSuccessorEdges().get(0).getDestination())) {
|
||||
return false;
|
||||
@@ -509,7 +453,7 @@ public class IfHelper {
|
||||
SequenceStatement sequence = (SequenceStatement)parent;
|
||||
|
||||
// build and cut the new else statement
|
||||
List<Statement> lst = new ArrayList<Statement>();
|
||||
List<Statement> lst = new ArrayList<>();
|
||||
for (int i = sequence.getStats().size() - 1; i >= 0; i--) {
|
||||
Statement sttemp = sequence.getStats().get(i);
|
||||
if (sttemp == ifstat) {
|
||||
@@ -597,7 +541,7 @@ public class IfHelper {
|
||||
SequenceStatement sequence = (SequenceStatement)parent;
|
||||
|
||||
// build and cut the new else statement
|
||||
List<Statement> lst = new ArrayList<Statement>();
|
||||
List<Statement> lst = new ArrayList<>();
|
||||
for (int i = sequence.getStats().size() - 1; i >= 0; i--) {
|
||||
Statement sttemp = sequence.getStats().get(i);
|
||||
if (sttemp == ifstat) {
|
||||
@@ -698,9 +642,7 @@ public class IfHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static Statement getNextStatement(Statement stat) {
|
||||
|
||||
Statement parent = stat.getParent();
|
||||
switch (parent.type) {
|
||||
case Statement.TYPE_ROOT:
|
||||
@@ -722,7 +664,6 @@ public class IfHelper {
|
||||
}
|
||||
|
||||
private static boolean existsPath(Statement from, Statement to) {
|
||||
|
||||
for (StatEdge edge : to.getAllPredecessorEdges()) {
|
||||
if (from.containsStatementStrict(edge.getSource())) {
|
||||
return true;
|
||||
@@ -734,17 +675,16 @@ public class IfHelper {
|
||||
|
||||
private static class IfNode {
|
||||
public final Statement value;
|
||||
public final List<IfNode> succs = new ArrayList<>();
|
||||
public final List<Integer> edgetypes = new ArrayList<>();
|
||||
|
||||
public final List<IfNode> succs = new ArrayList<IfNode>();
|
||||
public final List<Integer> edgetypes = new ArrayList<Integer>();
|
||||
|
||||
public IfNode(Statement value) {
|
||||
IfNode(Statement value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void addChild(IfNode child, int type) {
|
||||
succs.add(child);
|
||||
edgetypes.add(new Integer(type));
|
||||
edgetypes.add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user