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:
Lucas
2019-07-04 21:22:27 +02:00
632 changed files with 37505 additions and 13247 deletions

6
.editorconfig Normal file
View 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
View File

@@ -0,0 +1,3 @@
/.idea/
/.gradle/
/build/

84
README.md Normal file
View 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
View 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'
}
}

View File

@@ -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

Binary file not shown.

View 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
View 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
View 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

View File

@@ -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>

View File

@@ -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.

View File

@@ -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>";
}
}

View File

@@ -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"
};
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
// }
}
}

View File

@@ -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);

View File

@@ -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];
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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('>');
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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]++;
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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);
}

View File

@@ -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();
}
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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) { }

View File

@@ -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);
}
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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("[]");
}
}
}

View File

@@ -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()) {

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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 {
}
}
}
}
}

View File

@@ -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);
}

View File

@@ -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