Merge remote-tracking branch 'upstream/master'

master
Graham 4 years ago
commit 9c869a42c3
  1. 14
      build.gradle
  2. BIN
      gradle/wrapper/gradle-wrapper.jar
  3. 3
      gradle/wrapper/gradle-wrapper.properties
  4. 53
      gradlew
  5. 43
      gradlew.bat
  6. 6
      src/org/jetbrains/java/decompiler/code/CodeConstants.java
  7. 13
      src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java
  8. 13
      src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java
  9. 4
      src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java
  10. 4
      src/org/jetbrains/java/decompiler/main/AssertProcessor.java
  11. 4
      src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java
  12. 251
      src/org/jetbrains/java/decompiler/main/ClassWriter.java
  13. 84
      src/org/jetbrains/java/decompiler/main/ClassesProcessor.java
  14. 4
      src/org/jetbrains/java/decompiler/main/EnumProcessor.java
  15. 4
      src/org/jetbrains/java/decompiler/main/InitializerProcessor.java
  16. 36
      src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java
  17. 11
      src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java
  18. 6
      src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java
  19. 17
      src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java
  20. 21
      src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java
  21. 19
      src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java
  22. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java
  23. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java
  24. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java
  25. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java
  26. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java
  27. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java
  28. 29
      src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java
  29. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java
  30. 26
      src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java
  31. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java
  32. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java
  33. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java
  34. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java
  35. 32
      src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java
  36. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java
  37. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java
  38. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java
  39. 11
      src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java
  40. 6
      src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java
  41. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java
  42. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java
  43. 2
      src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java
  44. 5
      src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java
  45. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java
  46. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java
  47. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java
  48. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java
  49. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java
  50. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java
  51. 4
      src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java
  52. 10
      src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java
  53. 2
      src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java
  54. 7
      src/org/jetbrains/java/decompiler/struct/ContextUnit.java
  55. 124
      src/org/jetbrains/java/decompiler/struct/StructClass.java
  56. 7
      src/org/jetbrains/java/decompiler/struct/StructContext.java
  57. 33
      src/org/jetbrains/java/decompiler/struct/StructField.java
  58. 51
      src/org/jetbrains/java/decompiler/struct/StructMember.java
  59. 116
      src/org/jetbrains/java/decompiler/struct/StructMethod.java
  60. 36
      src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java
  61. 43
      src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java
  62. 100
      src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java
  63. 4
      src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java
  64. 4
      src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java
  65. 165
      src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java
  66. 37
      src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java
  67. 12
      src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java
  68. 5
      src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java
  69. 4
      src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java
  70. 4
      src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java
  71. 7
      src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java
  72. 4
      src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java
  73. 27
      src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java
  74. 14
      src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java
  75. 32
      src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java
  76. 4
      src/org/jetbrains/java/decompiler/util/InterpreterUtil.java
  77. 7
      src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java
  78. 6
      src/org/jetbrains/java/decompiler/util/TextUtil.java
  79. 25
      test/org/jetbrains/java/decompiler/SingleClassesTest.java
  80. BIN
      testData/classes/java9/module-info.class
  81. BIN
      testData/classes/pkg/TestInheritanceChainCycle.class
  82. BIN
      testData/classes/pkg/TestInvertedFloatComparison.class
  83. BIN
      testData/classes/pkg/package-info.class
  84. BIN
      testData/classes/records/TestRecordAnno.class
  85. BIN
      testData/classes/records/TestRecordEmpty.class
  86. BIN
      testData/classes/records/TestRecordGenericVararg.class
  87. BIN
      testData/classes/records/TestRecordSimple.class
  88. BIN
      testData/classes/records/TestRecordVararg.class
  89. 22
      testData/results/TestInheritanceChainCycle.dec
  90. 167
      testData/results/TestInvertedFloatComparison.dec
  91. 39
      testData/results/TestRecordAnno.dec
  92. 17
      testData/results/TestRecordEmpty.dec
  93. 39
      testData/results/TestRecordGenericVararg.dec
  94. 37
      testData/results/TestRecordSimple.dec
  95. 37
      testData/results/TestRecordVararg.dec
  96. 19
      testData/results/module-info.dec
  97. 4
      testData/results/package-info.dec
  98. 8
      testData/src/ext/PkgAnno.java
  99. 3
      testData/src/java9/sample.module/TestClass.java
  100. 8
      testData/src/java9/sample.module/TestModuleAnno.java
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,9 +1,9 @@
// 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.
// Copyright 2000-2021 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.
apply plugin: 'java'
compileJava {
sourceCompatibility '1.8'
targetCompatibility '1.8'
sourceCompatibility '11'
targetCompatibility '11'
}
sourceSets {
@ -11,14 +11,14 @@ sourceSets {
test.java.srcDirs 'test'
}
repositories { jcenter() }
repositories { mavenCentral() }
dependencies {
testCompile 'junit:junit:4.12'
testCompile 'org.assertj:assertj-core:3.12.2'
testImplementation 'junit:junit:4.12'
testImplementation 'org.assertj:assertj-core:3.12.2'
}
jar {
archiveName 'fernflower.jar'
archiveFileName = 'fernflower.jar'
manifest {
attributes 'Main-Class': 'org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler'
}

Binary file not shown.

@ -1,6 +1,5 @@
#Tue Nov 28 14:11:56 CET 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip

53
gradlew vendored

@ -1,5 +1,21 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# 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
#
# https://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.
#
##############################################################################
##
## Gradle start up script for UN*X
@ -28,7 +44,7 @@ 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=""
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@ -66,6 +82,7 @@ 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
@ -109,10 +126,11 @@ 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
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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
@ -138,19 +156,19 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $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" ;;
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
@ -159,14 +177,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
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" "$@"

43
gradlew.bat vendored

@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@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=
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@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
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -35,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -45,28 +64,14 @@ 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%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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"})
@ -64,6 +64,7 @@ public interface CodeConstants {
int ACC_STATIC = 0x0008;
int ACC_FINAL = 0x0010;
int ACC_SYNCHRONIZED = 0x0020;
int ACC_OPEN = 0x0020;
int ACC_NATIVE = 0x0100;
int ACC_ABSTRACT = 0x0400;
int ACC_STRICT = 0x0800;
@ -75,6 +76,7 @@ public interface CodeConstants {
int ACC_ANNOTATION = 0x2000;
int ACC_ENUM = 0x4000;
int ACC_MANDATED = 0x8000;
int ACC_MODULE = 0x8000;
// ----------------------------------------------------------------------
// CLASS FLAGS
@ -112,6 +114,8 @@ public interface CodeConstants {
int CONSTANT_MethodHandle = 15;
int CONSTANT_MethodType = 16;
int CONSTANT_InvokeDynamic = 18;
int CONSTANT_Module = 19;
int CONSTANT_Package = 20;
// ----------------------------------------------------------------------
// MethodHandle reference_kind values

@ -1,10 +1,11 @@
// 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.
// Copyright 2000-2021 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.*;
import org.jetbrains.java.decompiler.code.interpreter.InstructionImpact;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.struct.gen.DataPoint;
@ -96,9 +97,9 @@ public class ControlFlowGraph implements CodeConstants {
return buf.toString();
}
public void inlineJsr(StructMethod mt) {
public void inlineJsr(StructClass cl, StructMethod mt) {
processJsr();
removeJsr(mt);
removeJsr(cl, mt);
removeMarkers();
@ -432,7 +433,7 @@ public class ControlFlowGraph implements CodeConstants {
}
}
private static class JsrRecord {
private static final class JsrRecord {
private final BasicBlock jsr;
private final Set<BasicBlock> range;
private final BasicBlock ret;
@ -668,8 +669,8 @@ public class ControlFlowGraph implements CodeConstants {
}
}
private void removeJsr(StructMethod mt) {
removeJsrInstructions(mt.getClassStruct().getPool(), first, DataPoint.getInitialDataPoint(mt));
private void removeJsr(StructClass cl, StructMethod mt) {
removeJsrInstructions(cl.getPool(), first, DataPoint.getInitialDataPoint(mt));
}
private static void removeJsrInstructions(ConstantPool pool, BasicBlock block, DataPoint data) {

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -33,9 +33,16 @@ public class ExceptionRangeCFG {
StringBuilder buf = new StringBuilder();
buf.append("exceptionType:");
for (String exception_type : exceptionTypes) {
buf.append(" ").append(exception_type);
if (exceptionTypes == null) {
buf.append(" null");
}
else {
for (String exception_type : exceptionTypes) {
buf.append(" ").append(exception_type);
}
}
buf.append(new_line_separator);
buf.append("handler: ").append(handler.id).append(new_line_separator);

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -12,7 +12,7 @@ import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.ListStack;
public class InstructionImpact {
public final class InstructionImpact {
// {read, write}
private static final int[][][] stack_impact = {

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -21,7 +21,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class AssertProcessor {
public final class AssertProcessor {
private static final VarType CLASS_ASSERTION_ERROR = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/AssertionError");

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -22,7 +22,7 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.util.*;
import java.util.Map.Entry;
public class ClassReference14Processor {
public final class ClassReference14Processor {
private static final ExitExprent BODY_EXPR;
private static final ExitExprent HANDLER_EXPR;

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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;
@ -14,10 +14,7 @@ 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;
import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructField;
import org.jetbrains.java.decompiler.struct.StructMember;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.*;
import org.jetbrains.java.decompiler.struct.attr.*;
import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
@ -28,6 +25,7 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.TextBuffer;
import java.util.*;
import java.util.stream.Collectors;
public class ClassWriter {
private final PoolInterceptor interceptor;
@ -43,7 +41,7 @@ public class ClassWriter {
InitializerProcessor.extractInitializers(wrapper);
if (node.type == ClassNode.CLASS_ROOT &&
!cl.isVersionGE_1_5() &&
!cl.isVersion5() &&
DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) {
ClassReference14Processor.processClassReferences(node);
}
@ -162,11 +160,19 @@ public class ClassWriter {
dummy_tracer.incrementCurrentSourceLine(buffer.countLines(start_class_def));
List<StructRecordComponent> components = cl.getRecordComponents();
for (StructField fd : cl.getFields()) {
boolean hide = fd.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) ||
wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
if (hide) continue;
if (components != null && fd.getAccessFlags() == (CodeConstants.ACC_FINAL | CodeConstants.ACC_PRIVATE) &&
components.stream().anyMatch(c -> c.getName().equals(fd.getName()) && c.getDescriptor().equals(fd.getDescriptor()))) {
// Record component field: skip it
continue;
}
boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
if (isEnum) {
if (enumFields) {
@ -256,6 +262,117 @@ public class ClassWriter {
DecompilerContext.getLogger().endWriteClass();
}
@SuppressWarnings("SpellCheckingInspection")
private static boolean isSyntheticRecordMethod(StructClass cl, StructMethod mt, TextBuffer code) {
if (cl.getRecordComponents() != null) {
String name = mt.getName(), descriptor = mt.getDescriptor();
if (name.equals("equals") && descriptor.equals("(Ljava/lang/Object;)Z") ||
name.equals("hashCode") && descriptor.equals("()I") ||
name.equals("toString") && descriptor.equals("()Ljava/lang/String;")) {
if (code.countLines() == 1) {
String str = code.toString().trim();
return str.startsWith("return this." + name + "<invokedynamic>(this");
}
}
}
return false;
}
public static void packageInfoToJava(StructClass cl, TextBuffer buffer) {
appendAnnotations(buffer, 0, cl, -1);
int index = cl.qualifiedName.lastIndexOf('/');
String packageName = cl.qualifiedName.substring(0, index).replace('/', '.');
buffer.append("package ").append(packageName).append(';').appendLineSeparator().appendLineSeparator();
}
public static void moduleInfoToJava(StructClass cl, TextBuffer buffer) {
appendAnnotations(buffer, 0, cl, -1);
StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE);
if ((moduleAttribute.moduleFlags & CodeConstants.ACC_OPEN) != 0) {
buffer.append("open ");
}
buffer.append("module ").append(moduleAttribute.moduleName).append(" {").appendLineSeparator();
writeModuleInfoBody(buffer, moduleAttribute);
buffer.append('}').appendLineSeparator();
}
private static void writeModuleInfoBody(TextBuffer buffer, StructModuleAttribute moduleAttribute) {
boolean newLineNeeded = false;
List<StructModuleAttribute.RequiresEntry> requiresEntries = moduleAttribute.requires;
if (!requiresEntries.isEmpty()) {
for (StructModuleAttribute.RequiresEntry requires : requiresEntries) {
if (!isGenerated(requires.flags)) {
buffer.appendIndent(1).append("requires ").append(requires.moduleName.replace('/', '.')).append(';').appendLineSeparator();
newLineNeeded = true;
}
}
}
List<StructModuleAttribute.ExportsEntry> exportsEntries = moduleAttribute.exports;
if (!exportsEntries.isEmpty()) {
if (newLineNeeded) buffer.appendLineSeparator();
for (StructModuleAttribute.ExportsEntry exports : exportsEntries) {
if (!isGenerated(exports.flags)) {
buffer.appendIndent(1).append("exports ").append(exports.packageName.replace('/', '.'));
List<String> exportToModules = exports.exportToModules;
if (exportToModules.size() > 0) {
buffer.append(" to").appendLineSeparator();
appendFQClassNames(buffer, exportToModules);
}
buffer.append(';').appendLineSeparator();
newLineNeeded = true;
}
}
}
List<StructModuleAttribute.OpensEntry> opensEntries = moduleAttribute.opens;
if (!opensEntries.isEmpty()) {
if (newLineNeeded) buffer.appendLineSeparator();
for (StructModuleAttribute.OpensEntry opens : opensEntries) {
if (!isGenerated(opens.flags)) {
buffer.appendIndent(1).append("opens ").append(opens.packageName.replace('/', '.'));
List<String> opensToModules = opens.opensToModules;
if (opensToModules.size() > 0) {
buffer.append(" to").appendLineSeparator();
appendFQClassNames(buffer, opensToModules);
}
buffer.append(';').appendLineSeparator();
newLineNeeded = true;
}
}
}
List<String> usesEntries = moduleAttribute.uses;
if (!usesEntries.isEmpty()) {
if (newLineNeeded) buffer.appendLineSeparator();
for (String uses : usesEntries) {
buffer.appendIndent(1).append("uses ").append(ExprProcessor.buildJavaClassName(uses)).append(';').appendLineSeparator();
}
newLineNeeded = true;
}
List<StructModuleAttribute.ProvidesEntry> providesEntries = moduleAttribute.provides;
if (!providesEntries.isEmpty()) {
if (newLineNeeded) buffer.appendLineSeparator();
for (StructModuleAttribute.ProvidesEntry provides : providesEntries) {
buffer.appendIndent(1).append("provides ").append(ExprProcessor.buildJavaClassName(provides.interfaceName)).append(" with").appendLineSeparator();
appendFQClassNames(buffer, provides.implementationNames.stream().map(ExprProcessor::buildJavaClassName).collect(Collectors.toList()));
buffer.append(';').appendLineSeparator();
}
}
}
private static boolean isGenerated(int flags) {
return (flags & (CodeConstants.ACC_SYNTHETIC | CodeConstants.ACC_MANDATED)) != 0;
}
private static void addTracer(StructClass cls, StructMethod method, BytecodeMappingTracer tracer) {
StructLineNumberTableAttribute table = method.getAttribute(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE);
tracer.setLineNumberTable(table);
@ -302,6 +419,13 @@ public class ClassWriter {
flags &= ~CodeConstants.ACC_FINAL;
}
List<StructRecordComponent> components = cl.getRecordComponents();
if (components != null) {
// records are implicitly final
flags &= ~CodeConstants.ACC_FINAL;
}
appendModifiers(buffer, flags, CLASS_ALLOWED, isInterface, CLASS_EXCLUDED);
if (isEnum) {
@ -313,10 +437,12 @@ public class ClassWriter {
}
buffer.append("interface ");
}
else if (components != null) {
buffer.append("record ");
}
else {
buffer.append("class ");
}
buffer.append(node.simpleName);
GenericClassDescriptor descriptor = getGenericClassDescriptor(cl);
@ -324,9 +450,22 @@ public class ClassWriter {
appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds);
}
if (components != null) {
buffer.append('(');
for (int i = 0; i < components.size(); i++) {
StructRecordComponent cd = components.get(i);
if (i > 0) {
buffer.append(", ");
}
boolean varArgComponent = i == components.size() - 1 && isVarArgRecord(cl);
recordComponentToJava(cd, buffer, varArgComponent);
}
buffer.append(')');
}
buffer.append(' ');
if (!isEnum && !isInterface && cl.superClass != null) {
if (!isEnum && !isInterface && components == null && cl.superClass != null) {
VarType supertype = new VarType(cl.superClass.getString(), true);
if (!VarType.VARTYPE_OBJECT.equals(supertype)) {
buffer.append("extends ");
@ -362,6 +501,13 @@ public class ClassWriter {
buffer.append('{').appendLineSeparator();
}
private static boolean isVarArgRecord(StructClass cl) {
String canonicalConstructorDescriptor =
cl.getRecordComponents().stream().map(c -> c.getDescriptor()).collect(Collectors.joining("", "(", ")V"));
StructMethod init = cl.getMethod(CodeConstants.INIT_NAME, canonicalConstructorDescriptor);
return init != null && init.hasModifier(CodeConstants.ACC_VARARGS);
}
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);
@ -389,15 +535,9 @@ public class ClassWriter {
appendModifiers(buffer, fd.getAccessFlags(), FIELD_ALLOWED, isInterface, FIELD_EXCLUDED);
}
VarType fieldType = new VarType(fd.getDescriptor(), false);
GenericFieldDescriptor descriptor = null;
if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
StructGenericSignatureAttribute attr = fd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE);
if (attr != null) {
descriptor = GenericMain.parseFieldSignature(attr.getSignature());
}
}
Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(fd);
VarType fieldType = fieldTypeData.getKey();
GenericFieldDescriptor descriptor = fieldTypeData.getValue();
if (!isEnum) {
if (descriptor != null) {
@ -452,6 +592,27 @@ public class ClassWriter {
}
}
private static void recordComponentToJava(StructRecordComponent cd, TextBuffer buffer, boolean varArgComponent) {
appendAnnotations(buffer, -1, cd, TypeAnnotation.FIELD);
Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(cd);
VarType fieldType = fieldTypeData.getKey();
GenericFieldDescriptor descriptor = fieldTypeData.getValue();
if (descriptor != null) {
buffer.append(GenericMain.getGenericCastTypeName(varArgComponent ? descriptor.type.decreaseArrayDim() : descriptor.type));
}
else {
buffer.append(ExprProcessor.getCastTypeName(varArgComponent ? fieldType.decreaseArrayDim() : fieldType));
}
if (varArgComponent) {
buffer.append("...");
}
buffer.append(' ');
buffer.append(cd.getName());
}
private static void methodLambdaToJava(ClassNode lambdaNode,
ClassWrapper classWrapper,
StructMethod mt,
@ -579,7 +740,7 @@ public class ClassWriter {
boolean isAnnotation = cl.hasModifier(CodeConstants.ACC_ANNOTATION);
boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
boolean isDeprecated = mt.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED);
boolean clinit = false, init = false, dinit = false;
boolean clInit = false, init = false, dInit = false;
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
@ -624,7 +785,7 @@ public class ClassWriter {
if (CodeConstants.INIT_NAME.equals(name)) {
if (node.type == ClassNode.CLASS_ANONYMOUS) {
name = "";
dinit = true;
dInit = true;
}
else {
name = node.simpleName;
@ -633,7 +794,7 @@ public class ClassWriter {
}
else if (CodeConstants.CLINIT_NAME.equals(name)) {
name = "";
clinit = true;
clInit = true;
}
GenericMethodDescriptor descriptor = null;
@ -662,7 +823,7 @@ public class ClassWriter {
boolean throwsExceptions = false;
int paramCount = 0;
if (!clinit && !dinit) {
if (!clInit && !dInit) {
boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC);
if (descriptor != null && !descriptor.typeParameters.isEmpty()) {
@ -795,7 +956,7 @@ public class ClassWriter {
buffer.appendLineSeparator();
}
else {
if (!clinit && !dinit) {
if (!clInit && !dInit) {
buffer.append(' ');
}
@ -811,7 +972,8 @@ public class ClassWriter {
BytecodeMappingTracer codeTracer = new BytecodeMappingTracer(tracer.getCurrentSourceLine());
TextBuffer code = root.toJava(indent + 1, codeTracer);
hideMethod = (code.length() == 0) && (clinit || dinit || hideConstructor(node, init, throwsExceptions, paramCount, flags));
hideMethod = code.length() == 0 && (clInit || dInit || hideConstructor(node, init, throwsExceptions, paramCount, flags)) ||
isSyntheticRecordMethod(cl, mt, code);
buffer.append(code);
@ -851,7 +1013,6 @@ public class ClassWriter {
}
private static boolean hideConstructor(ClassNode node, boolean init, boolean throwsExceptions, int paramCount, int methodAccessFlags) {
if (!init || throwsExceptions || paramCount > 0 || !DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) {
return false;
}
@ -859,11 +1020,11 @@ public class ClassWriter {
ClassWrapper wrapper = node.getWrapper();
StructClass cl = wrapper.getClassStruct();
int classAccesFlags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access;
int classAccessFlags = 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))) {
if(!isEnum && ((classAccessFlags & ACCESSIBILITY_FLAGS) != (methodAccessFlags & ACCESSIBILITY_FLAGS))) {
return false;
}
@ -879,6 +1040,20 @@ public class ClassWriter {
return true;
}
private static Map.Entry<VarType, GenericFieldDescriptor> getFieldTypeData(StructField fd) {
VarType fieldType = new VarType(fd.getDescriptor(), false);
GenericFieldDescriptor descriptor = null;
if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
StructGenericSignatureAttribute attr = fd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE);
if (attr != null) {
descriptor = GenericMain.parseFieldSignature(attr.getSignature());
}
}
return new AbstractMap.SimpleImmutableEntry<>(fieldType, descriptor);
}
private static void appendDeprecation(TextBuffer buffer, int indent) {
buffer.appendIndent(indent).append("/** @deprecated */").appendLineSeparator();
}
@ -937,11 +1112,11 @@ public class ClassWriter {
buffer.appendIndent(indent).append("// $FF: ").append(comment).appendLineSeparator();
}
private static final StructGeneralAttribute.Key[] 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 = {
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 = {
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, int indent, StructMember mb, int targetType) {
@ -953,7 +1128,13 @@ public class ClassWriter {
for (AnnotationExprent annotation : attribute.getAnnotations()) {
String text = annotation.toJava(indent, BytecodeMappingTracer.DUMMY).toString();
filter.add(text);
buffer.append(text).appendLineSeparator();
buffer.append(text);
if (indent < 0) {
buffer.append(' ');
}
else {
buffer.appendLineSeparator();
}
}
}
}
@ -1079,4 +1260,14 @@ public class ClassWriter {
buffer.append('>');
}
private static void appendFQClassNames(TextBuffer buffer, List<String> names) {
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
buffer.appendIndent(2).append(name);
if (i < names.size() - 1) {
buffer.append(',').appendLineSeparator();
}
}
}
}

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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;
@ -245,7 +245,7 @@ public class ClassesProcessor implements CodeConstants {
}
try {
mt.expandData();
mt.expandData(enclosingCl);
InstructionSequence seq = mt.getInstructionSequence();
if (seq != null) {
@ -302,55 +302,65 @@ public class ClassesProcessor implements CodeConstants {
return;
}
boolean packageInfo = cl.isSynthetic() && "package-info".equals(root.simpleName);
boolean moduleInfo = cl.hasModifier(CodeConstants.ACC_MODULE) && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE);
DecompilerContext.getLogger().startReadingClass(cl.qualifiedName);
try {
ImportCollector importCollector = new ImportCollector(root);
DecompilerContext.startClass(importCollector);
new LambdaProcessor().processClass(root);
if (packageInfo) {
ClassWriter.packageInfoToJava(cl, buffer);
// add simple class names to implicit import
addClassnameToImport(root, importCollector);
importCollector.writeImports(buffer, false);
}
else if (moduleInfo) {
TextBuffer moduleBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
ClassWriter.moduleInfoToJava(cl, moduleBuffer);
// build wrappers for all nested classes (that's where actual processing takes place)
initWrappers(root);
importCollector.writeImports(buffer, true);
new NestedClassProcessor().processClass(root, root);
buffer.append(moduleBuffer);
}
else {
new LambdaProcessor().processClass(root);
new NestedMemberAccess().propagateMemberAccess(root);
// add simple class names to implicit import
addClassNameToImport(root, importCollector);
TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
new ClassWriter().classToJava(root, classBuffer, 0, null);
// build wrappers for all nested classes (that's where actual processing takes place)
initWrappers(root);
int index = cl.qualifiedName.lastIndexOf("/");
if (index >= 0) {
String packageName = cl.qualifiedName.substring(0, index).replace('/', '.');
new NestedClassProcessor().processClass(root, root);
buffer.append("package ");
buffer.append(packageName);
buffer.append(";");
buffer.appendLineSeparator();
buffer.appendLineSeparator();
}
new NestedMemberAccess().propagateMemberAccess(root);
int import_lines_written = importCollector.writeImports(buffer);
if (import_lines_written > 0) {
buffer.appendLineSeparator();
}
TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
new ClassWriter().classToJava(root, classBuffer, 0, null);
int offsetLines = buffer.countLines();
int index = cl.qualifiedName.lastIndexOf('/');
if (index >= 0) {
String packageName = cl.qualifiedName.substring(0, index).replace('/', '.');
buffer.append("package ").append(packageName).append(';').appendLineSeparator().appendLineSeparator();
}
buffer.append(classBuffer);
importCollector.writeImports(buffer, true);
if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) {
BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper();
mapper.addTotalOffset(offsetLines);
if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_ORIGINAL_LINES)) {
buffer.dumpOriginalLineNumbers(mapper.getOriginalLinesMapping());
}
if (DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE)) {
buffer.appendLineSeparator();
mapper.dumpMapping(buffer, true);
int offsetLines = buffer.countLines();
buffer.append(classBuffer);
if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) {
BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper();
mapper.addTotalOffset(offsetLines);
if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_ORIGINAL_LINES)) {
buffer.dumpOriginalLineNumbers(mapper.getOriginalLinesMapping());
}
if (DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE)) {
buffer.appendLineSeparator();
mapper.dumpMapping(buffer, true);
}
}
}
}
@ -375,13 +385,13 @@ public class ClassesProcessor implements CodeConstants {
}
}
private static void addClassnameToImport(ClassNode node, ImportCollector imp) {
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);
}
for (ClassNode nd : node.nested) {
addClassnameToImport(nd, imp);
addClassNameToImport(nd, imp);
}
}

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -13,7 +13,7 @@ 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 final class EnumProcessor {
public static void clearEnum(ClassWrapper wrapper) {
StructClass cl = wrapper.getClassStruct();

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -18,7 +18,7 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.util.ArrayList;
import java.util.List;
public class InitializerProcessor {
public final class InitializerProcessor {
public static void extractInitializers(ClassWrapper wrapper) {
MethodWrapper method = wrapper.getMethodWrapper(CodeConstants.CLINIT_NAME, "()V");
if (method != null && method.root != null) { // successfully decompiled static constructor

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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.ClassNode;
@ -39,8 +39,10 @@ public class ImportCollector {
Map<String, StructClass> classes = DecompilerContext.getStructContext().getClasses();
LinkedList<String> queue = new LinkedList<>();
Set<StructClass> processedClasses = new HashSet<>();
StructClass currentClass = root.classStruct;
while (currentClass != null) {
processedClasses.add(currentClass);
if (currentClass.superClass != null) {
queue.add(currentClass.superClass.getString());
}
@ -63,10 +65,17 @@ public class ImportCollector {
}
// .. and traverse through parent.
currentClass = !queue.isEmpty() ? classes.get(queue.removeFirst()) : null;
while (currentClass == null && !queue.isEmpty()) {
currentClass = classes.get(queue.removeFirst());
}
do {
currentClass = queue.isEmpty() ? null : classes.get(queue.removeFirst());
if (currentClass != null && processedClasses.contains(currentClass)) {
// Class already processed, skipping.
// This may be sign of circularity in the class hierarchy but in most cases this mean that same interface
// are listed as implemented several times in the class hierarchy.
currentClass = null;
}
} while (currentClass == null && !queue.isEmpty());
}
}
@ -150,21 +159,14 @@ public class ImportCollector {
return result == null ? shortName : result;
}
public int writeImports(TextBuffer buffer) {
int importLinesWritten = 0;
public void writeImports(TextBuffer buffer, boolean addSeparator) {
List<String> imports = packImports();
for (String s : imports) {
buffer.append("import ");
buffer.append(s);
buffer.append(';');
for (String line : imports) {
buffer.append("import ").append(line).append(';').appendLineSeparator();
}
if (addSeparator && !imports.isEmpty()) {
buffer.appendLineSeparator();
importLinesWritten++;
}
return importLinesWritten;
}
private List<String> packImports() {

@ -25,17 +25,6 @@ public class BaseDecompiler {
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 {
engine.decompileContext();

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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;
@ -60,10 +60,10 @@ public class ClassWrapper {
try {
if (mt.containsCode()) {
if (maxSec == 0 || testMode) {
root = MethodProcessorRunnable.codeToJava(mt, md, varProc);
root = MethodProcessorRunnable.codeToJava(classStruct, mt, md, varProc);
}
else {
MethodProcessorRunnable mtProc = new MethodProcessorRunnable(mt, md, varProc, DecompilerContext.getCurrentContext());
MethodProcessorRunnable mtProc = new MethodProcessorRunnable(classStruct, mt, md, varProc, DecompilerContext.getCurrentContext());
Thread mtThread = new Thread(mtProc, "Java decompiler");
long stopAt = System.currentTimeMillis() + maxSec * 1000L;

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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;
@ -33,17 +33,16 @@ public class LambdaProcessor {
ClassesProcessor clProcessor = DecompilerContext.getClassProcessor();
StructClass cl = node.classStruct;
if (cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lambda beginning with Java 8
if (!cl.isVersion8()) { // lambda beginning with Java 8
return;
}
StructBootstrapMethodsAttribute bootstrap =
cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
StructBootstrapMethodsAttribute bootstrap = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);
if (bootstrap == null || bootstrap.getMethodsNumber() == 0) {
return; // no bootstrap constants in pool
}
BitSet lambda_methods = new BitSet();
BitSet lambdaMethods = new BitSet();
// find lambda bootstrap constants
for (int i = 0; i < bootstrap.getMethodsNumber(); ++i) {
@ -52,11 +51,11 @@ public class LambdaProcessor {
// FIXME: extend for Eclipse etc. at some point
if (JAVAC_LAMBDA_CLASS.equals(method_ref.classname) &&
(JAVAC_LAMBDA_METHOD.equals(method_ref.elementname) || JAVAC_LAMBDA_ALT_METHOD.equals(method_ref.elementname))) {
lambda_methods.set(i);
lambdaMethods.set(i);
}
}
if (lambda_methods.isEmpty()) {
if (lambdaMethods.isEmpty()) {
return; // no lambda bootstrap constant found
}
@ -64,7 +63,7 @@ public class LambdaProcessor {
// iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes.
for (StructMethod mt : cl.getMethods()) {
mt.expandData();
mt.expandData(cl);
InstructionSequence seq = mt.getInstructionSequence();
if (seq != null && seq.length() > 0) {
@ -76,7 +75,7 @@ public class LambdaProcessor {
if (instr.opcode == CodeConstants.opc_invokedynamic) {
LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.operand(0));
if (lambda_methods.get(invoke_dynamic.index1)) { // lambda invocation found
if (lambdaMethods.get(invoke_dynamic.index1)) { // lambda invocation found
List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1);
MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor);

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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,6 +22,7 @@ import java.io.IOException;
public class MethodProcessorRunnable implements Runnable {
public final Object lock = new Object();
private final StructClass klass;
private final StructMethod method;
private final MethodDescriptor methodDescriptor;
private final VarProcessor varProc;
@ -31,10 +32,12 @@ public class MethodProcessorRunnable implements Runnable {
private volatile Throwable error;
private volatile boolean finished = false;
public MethodProcessorRunnable(StructMethod method,
public MethodProcessorRunnable(StructClass klass,
StructMethod method,
MethodDescriptor methodDescriptor,
VarProcessor varProc,
DecompilerContext parentContext) {
this.klass = klass;
this.method = method;
this.methodDescriptor = methodDescriptor;
this.varProc = varProc;
@ -48,7 +51,7 @@ public class MethodProcessorRunnable implements Runnable {
try {
DecompilerContext.setCurrentContext(parentContext);
root = codeToJava(method, methodDescriptor, varProc);
root = codeToJava(klass, method, methodDescriptor, varProc);
}
catch (Throwable t) {
error = t;
@ -63,17 +66,15 @@ public class MethodProcessorRunnable implements Runnable {
}
}
public static RootStatement codeToJava(StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException {
StructClass cl = mt.getClassStruct();
public static RootStatement codeToJava(StructClass cl, StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException {
boolean isInitializer = CodeConstants.CLINIT_NAME.equals(mt.getName()); // for now static initializer only
mt.expandData();
mt.expandData(cl);
InstructionSequence seq = mt.getInstructionSequence();
ControlFlowGraph graph = new ControlFlowGraph(seq);
DeadCodeHelper.removeDeadBlocks(graph);
graph.inlineJsr(mt);
graph.inlineJsr(cl, mt);
// TODO: move to the start, before jsr inlining
DeadCodeHelper.connectDummyExitBlock(graph);
@ -110,13 +111,13 @@ public class MethodProcessorRunnable implements Runnable {
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());
ExceptionDeobfuscator.insertDummyExceptionHandlerBlocks(graph, mt.getBytecodeVersion());
}
RootStatement root = DomHelper.parseGraph(graph);
FinallyProcessor fProc = new FinallyProcessor(md, varProc);
while (fProc.iterateGraph(mt, root, graph)) {
while (fProc.iterateGraph(cl, mt, root, graph)) {
root = DomHelper.parseGraph(graph);
}

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -13,7 +13,7 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import java.util.*;
public class DeadCodeHelper {
public final class DeadCodeHelper {
public static void removeDeadBlocks(ControlFlowGraph graph) {
@ -267,7 +267,9 @@ public class DeadCodeHelper {
for (BasicBlock block : range.getProtectedRange()) {
setPreds.addAll(block.getPreds());
}
setPreds.removeAll(range.getProtectedRange());
for (BasicBlock basicBlock : range.getProtectedRange()) {
setPreds.remove(basicBlock);
}
if(setPreds.size() != 1) {
continue; // multiple predecessors, obfuscated range
@ -280,8 +282,7 @@ public class DeadCodeHelper {
}
boolean monitorexit_in_range = false;
Set<BasicBlock> setProtectedBlocks = new HashSet<>();
setProtectedBlocks.addAll(range.getProtectedRange());
Set<BasicBlock> setProtectedBlocks = new HashSet<>(range.getProtectedRange());
setProtectedBlocks.add(range.getHandler());
for (BasicBlock block : setProtectedBlocks) {
@ -305,7 +306,9 @@ public class DeadCodeHelper {
for (BasicBlock block : range.getProtectedRange()) {
setSuccs.addAll(block.getSuccs());
}
setSuccs.removeAll(range.getProtectedRange());
for (BasicBlock basicBlock : range.getProtectedRange()) {
setSuccs.remove(basicBlock);
}
if(setSuccs.size() != 1) {
continue; // non-unique successor
@ -461,7 +464,9 @@ public class DeadCodeHelper {
}
// add exception ranges from predecessors
setPredHandlersIntersection.removeAll(block.getSuccExceptions());
for (BasicBlock basicBlock : block.getSuccExceptions()) {
setPredHandlersIntersection.remove(basicBlock);
}
BasicBlock predecessor = block.getPreds().get(0);
for (BasicBlock handler : setPredHandlersIntersection) {

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -9,7 +9,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class ClasspathHelper {
public final class ClasspathHelper {
private static final Map<String, Method> METHOD_CACHE = Collections.synchronizedMap(new HashMap<>());

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -7,7 +7,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import java.util.LinkedList;
public class ClearStructHelper {
public final class ClearStructHelper {
public static void clearStatements(RootStatement root) {

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -13,7 +13,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class ConcatenationHelper {
public final class ConcatenationHelper {
private static final String builderClass = "java/lang/StringBuilder";
private static final String bufferClass = "java/lang/StringBuffer";

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -10,7 +10,7 @@ import java.util.List;
import java.util.Set;
public class DecHelper {
public final class DecHelper {
public static boolean checkStatementExceptions(List<? extends Statement> lst) {

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -16,7 +16,7 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.util.*;
public class DomHelper {
public final class DomHelper {
private static RootStatement graphToStatement(ControlFlowGraph graph) {

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -13,7 +13,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class ExitHelper {
public final class ExitHelper {
public static boolean condenseExits(RootStatement root) {
int changed = integrateExits(root);

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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;
@ -26,6 +26,7 @@ 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.vars.VarProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType;
@ -46,12 +47,8 @@ public class FinallyProcessor {
varProcessor = varProc;
}
public boolean iterateGraph(StructMethod mt, RootStatement root, ControlFlowGraph graph) {
return processStatementEx(mt, root, graph);
}
private boolean processStatementEx(StructMethod mt, RootStatement root, ControlFlowGraph graph) {
int bytecode_version = mt.getClassStruct().getBytecodeVersion();
public boolean iterateGraph(StructClass cl, StructMethod mt, RootStatement root, ControlFlowGraph graph) {
int bytecodeVersion = mt.getBytecodeVersion();
LinkedList<Statement> stack = new LinkedList<>();
stack.add(root);
@ -77,22 +74,20 @@ public class FinallyProcessor {
fin.setMonitor(var == null ? null : new VarExprent(var, VarType.VARTYPE_INT, varProcessor));
}
else {
Record inf = getFinallyInformation(mt, root, fin);
Record inf = getFinallyInformation(cl, 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, bytecodeVersion);
int varindex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER);
insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf, bytecode_version);
finallyBlockIDs.put(handler.id, varindex);
finallyBlockIDs.put(handler.id, varIndex);
}
DeadCodeHelper.removeDeadBlocks(graph); // e.g. multiple return blocks after a nested finally
@ -110,7 +105,7 @@ public class FinallyProcessor {
return false;
}
private static class Record {
private static final class Record {
private final int firstCode;
private final Map<BasicBlock, Boolean> mapLast;
@ -120,7 +115,7 @@ public class FinallyProcessor {
}
}
private Record getFinallyInformation(StructMethod mt, RootStatement root, CatchAllStatement fstat) {
private Record getFinallyInformation(StructClass cl, StructMethod mt, RootStatement root, CatchAllStatement fstat) {
Map<BasicBlock, Boolean> mapLast = new HashMap<>();
BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead();
@ -138,7 +133,7 @@ public class FinallyProcessor {
}
ExprProcessor proc = new ExprProcessor(methodDescriptor, varProcessor);
proc.processStatement(root, mt, mt.getClassStruct());
proc.processStatement(root, mt, cl);
SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
ssa.splitVariables(root, mt);
@ -527,7 +522,7 @@ public class FinallyProcessor {
return true;
}
private static class Area {
private static final class Area {
private final BasicBlock start;
private final Set<BasicBlock> sample;
private final BasicBlock next;

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -14,7 +14,7 @@ import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import java.util.List;
public class IdeaNotNullHelper {
public final class IdeaNotNullHelper {
public static boolean removeHardcodedChecks(Statement root, StructMethod mt) {

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -11,7 +11,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import java.util.*;
public class IfHelper {
public final class IfHelper {
public static boolean mergeAllIfs(RootStatement root) {
boolean res = mergeAllIfsRec(root, new HashSet<>());
if (res) {
@ -31,7 +31,8 @@ public class IfHelper {
res |= mergeAllIfsRec(st, setReorderedIfs);
// collapse composed if's
if (changed = mergeIfs(st, setReorderedIfs)) {
if (mergeIfs(st, setReorderedIfs)) {
changed = true;
break;
}
}
@ -75,21 +76,25 @@ public class IfHelper {
continue;
}
if (updated = collapseIfIf(rtnode)) {
if (collapseIfIf(rtnode)) {
updated = true;
break;
}
if (!setReorderedIfs.contains(stat.id)) {
if (updated = collapseIfElse(rtnode)) {
if (collapseIfElse(rtnode)) {
updated = true;
break;
}
if (updated = collapseElse(rtnode)) {
if (collapseElse(rtnode)) {
updated = true;
break;
}
}
if (updated = reorderIf((IfStatement)stat)) {
if (reorderIf((IfStatement)stat)) {
updated = true;
setReorderedIfs.add(stat.id);
break;
}
@ -440,10 +445,9 @@ public class IfHelper {
if (sttemp == ifstat) {
break;
}
else {
if (elsedirectpath = existsPath(sttemp, next)) {
break;
}
else if (existsPath(sttemp, next)) {
elsedirectpath = true;
break;
}
}
}

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.*;
@ -7,7 +7,7 @@ import java.util.ArrayList;
import java.util.List;
public class InlineSingleBlockHelper {
public final class InlineSingleBlockHelper {
public static boolean inlineSingleBlocks(RootStatement root) {

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -8,7 +8,7 @@ import java.util.*;
import java.util.Map.Entry;
public class LabelHelper {
public final class LabelHelper {
public static void cleanUpEdges(RootStatement root) {

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.DoStatement;
@ -11,7 +11,7 @@ import java.util.Arrays;
import java.util.Set;
public class LoopExtractHelper {
public final class LoopExtractHelper {
public static boolean extractLoops(Statement root) {

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -13,7 +13,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class MergeHelper {
public final class MergeHelper {
public static void enhanceLoops(Statement root) {
while (enhanceLoopsRec(root)) /**/;
SequenceHelper.condenseSequences(root);

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -16,7 +16,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
public class SecondaryFunctionsHelper {
public final class SecondaryFunctionsHelper {
private static final int[] funcsnot = new int[]{
FunctionExprent.FUNCTION_NE,
@ -170,6 +170,17 @@ public class SecondaryFunctionsHelper {
}
if (desttype >= 0) {
if (functype != FunctionExprent.FUNCTION_LCMP) {
boolean oneForNan = functype == FunctionExprent.FUNCTION_DCMPL || functype == FunctionExprent.FUNCTION_FCMPL;
boolean trueForOne = desttype == FunctionExprent.FUNCTION_LT || desttype == FunctionExprent.FUNCTION_LE;
boolean trueForNan = oneForNan == trueForOne;
if (trueForNan) {
List<Exprent> operands = new ArrayList<>();
operands.add(new FunctionExprent(funcsnot[desttype - FunctionExprent.FUNCTION_EQ],
funcexpr.getLstOperands(), funcexpr.bytecode));
return new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, operands, funcexpr.bytecode);
}
}
return new FunctionExprent(desttype, funcexpr.getLstOperands(), funcexpr.bytecode);
}
}
@ -378,6 +389,7 @@ public class SecondaryFunctionsHelper {
FunctionExprent fparam = (FunctionExprent)param;
int ftype = fparam.getFuncType();
boolean canSimplify = false;
switch (ftype) {
case FunctionExprent.FUNCTION_BOOL_NOT:
Exprent newexpr = fparam.getLstOperands().get(0);
@ -394,12 +406,24 @@ public class SecondaryFunctionsHelper {
}
case FunctionExprent.FUNCTION_EQ:
case FunctionExprent.FUNCTION_NE:
canSimplify = true;
case FunctionExprent.FUNCTION_LT:
case FunctionExprent.FUNCTION_GE:
case FunctionExprent.FUNCTION_GT:
case FunctionExprent.FUNCTION_LE:
fparam.setFuncType(funcsnot[ftype - FunctionExprent.FUNCTION_EQ]);
return fparam;
if (!canSimplify) {
operands = fparam.getLstOperands();
VarType left = operands.get(0).getExprType();
VarType right = operands.get(1).getExprType();
VarType commonSupertype = VarType.getCommonSupertype(left, right);
if (commonSupertype != null) {
canSimplify = commonSupertype.type != CodeConstants.TYPE_FLOAT && commonSupertype.type != CodeConstants.TYPE_DOUBLE;
}
}
if (canSimplify) {
fparam.setFuncType(funcsnot[ftype - FunctionExprent.FUNCTION_EQ]);
return fparam;
}
}
}
}

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -13,7 +13,7 @@ import java.util.HashSet;
import java.util.List;
public class SequenceHelper {
public final class SequenceHelper {
public static void condenseSequences(Statement root) {

@ -90,7 +90,9 @@ public class StrongConnectivityHelper {
for (Statement stat : lst) {
set.addAll(stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD));
}
set.removeAll(lst);
for (Statement stat : lst) {
set.remove(stat);
}
return (set.size() == 0);
}

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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;
@ -14,7 +14,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SwitchHelper {
public final class SwitchHelper {
public static void simplify(SwitchStatement switchStatement) {
SwitchExprent switchExprent = (SwitchExprent)switchStatement.getHeadexprent();
Exprent value = switchExprent.getValue();

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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.decompose;
import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
@ -127,16 +127,15 @@ public class DominatorTreeExceptionFilter {
Set<Integer> range = entry.getValue();
if (range.contains(id)) {
Integer exit;
if (!range.contains(childid)) {
exit = childid;
}
else if (map.containsKey(handler)) {
exit = -1;
}
else {
// after replacing 'new Integer(-1)' with '-1' Eclipse throws a NullPointerException on the following line
// could be a bug in Eclipse or some obscure specification glitch, FIXME: needs further investigation
exit = map.containsKey(handler) ? new Integer(-1) : mapChild.get(handler);
exit = mapChild.get(handler);
}
if (exit != null) {

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.deobfuscator;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -18,9 +18,9 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil;
import java.util.*;
import java.util.Map.Entry;
public class ExceptionDeobfuscator {
public final class ExceptionDeobfuscator {
private static class Range {
private static final class Range {
private final BasicBlock handler;
private final String uniqueStr;
private final Set<BasicBlock> protectedRange;

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.deobfuscator;
import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
@ -10,7 +10,7 @@ import java.util.HashSet;
import java.util.Set;
public class IrreducibleCFGDeobfuscator {
public final class IrreducibleCFGDeobfuscator {
public static boolean isStatementIrreducible(Statement statement) {

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.exps;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -13,7 +13,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ExprUtil {
public final class ExprUtil {
public static List<VarVersionPair> getSyntheticParametersMask(String className, String descriptor, int parameters) {
ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(className);
return node != null ? getSyntheticParametersMask(node, descriptor, parameters) : null;

@ -26,7 +26,7 @@ import java.util.Set;
public class NewExprent extends Exprent {
private InvocationExprent constructor;
private final VarType newType;
private List<Exprent> lstDims = new ArrayList<>();
private final List<Exprent> lstDims;
private List<Exprent> lstArrayElements = new ArrayList<>();
private boolean directArrayInit;
private boolean isVarArgParam;

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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.exps;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -31,7 +31,6 @@ import java.util.ArrayList;
import java.util.List;
public class VarExprent extends Exprent {
public static final int STACK_BASE = 10000;
public static final String VAR_NAMELESS_ENCLOSURE = "<VAR_NAMELESS_ENCLOSURE>";
@ -149,7 +148,7 @@ public class VarExprent extends Exprent {
Integer origIndex = processor.getVarOriginalIndex(index);
if (origIndex != null) {
String name = attr.getName(origIndex, visibleOffset);
if (name != null && TextUtil.isValidIdentifier(name, method.getClassStruct().getBytecodeVersion())) {
if (name != null && TextUtil.isValidIdentifier(name, method.getBytecodeVersion())) {
return name;
}
}

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.sforms;
import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
@ -472,7 +472,7 @@ public class FlattenStatementsHelper {
return mapDestinationNodes;
}
public static class FinallyPathWrapper {
public static final class FinallyPathWrapper {
public final String source;
public final String destination;
public final String entry;

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.stats;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -17,7 +17,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class CatchAllStatement extends Statement {
public final class CatchAllStatement extends Statement {
private Statement handler;

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.stats;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -17,7 +17,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class CatchStatement extends Statement {
public final class CatchStatement extends Statement {
private final List<List<String>> exctstrings = new ArrayList<>();
private final List<VarExprent> vars = new ArrayList<>();

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.stats;
import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;
@ -11,7 +11,7 @@ import java.util.ArrayList;
import java.util.List;
public class DoStatement extends Statement {
public final class DoStatement extends Statement {
public static final int LOOP_DO = 0;
public static final int LOOP_DOWHILE = 1;

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.stats;
import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;
@ -17,7 +17,7 @@ import java.util.ArrayList;
import java.util.List;
public class IfStatement extends Statement {
public final class IfStatement extends Statement {
public static final int IFTYPE_IF = 0;
public static final int IFTYPE_IFELSE = 1;

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.stats;
import org.jetbrains.java.decompiler.main.rels.ClassWrapper;
@ -8,7 +8,7 @@ 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.vars.VarVersionPair;
public class Statements {
public final class Statements {
public static Statement findFirstData(Statement stat) {
if (stat.getExprents() != null) {
return stat;

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.stats;
import org.jetbrains.java.decompiler.code.SwitchInstruction;
@ -19,7 +19,7 @@ import org.jetbrains.java.decompiler.util.TextBuffer;
import java.util.*;
public class SwitchStatement extends Statement {
public final class SwitchStatement extends Statement {
// *****************************************************************************
// private fields

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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.vars;
import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
@ -55,7 +55,7 @@ public class VarProcessor {
Integer index = mapOriginalVarIndices.get(pair.var);
if (index != null) {
String debugName = mapDebugVarNames.get(index);
if (debugName != null && TextUtil.isValidIdentifier(debugName, method.getClassStruct().getBytecodeVersion())) {
if (debugName != null && TextUtil.isValidIdentifier(debugName, method.getBytecodeVersion())) {
name = debugName;
}
}
@ -72,11 +72,7 @@ public class VarProcessor {
}
public Integer getVarOriginalIndex(int index) {
if (varVersions == null) {
return null;
}
return varVersions.getMapOriginalVarIndices().get(index);
return varVersions == null ? null : varVersions.getMapOriginalVarIndices().get(index);
}
public void refreshVarNames(VarNamesCollector vc) {

@ -170,7 +170,7 @@ public class VarTypeProcessor {
case Exprent.EXPRENT_VAR:
VarVersionPair pair = null;
if (exprent.type == Exprent.EXPRENT_CONST) {
pair = new VarVersionPair(((ConstExprent)exprent).id, -1);
pair = new VarVersionPair(exprent.id, -1);
}
else if (exprent.type == Exprent.EXPRENT_VAR) {
pair = new VarVersionPair((VarExprent)exprent);

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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.struct;
import org.jetbrains.java.decompiler.main.DecompilerContext;
@ -65,7 +65,7 @@ public class ContextUnit {
StructClass newCl;
try (DataInputFullStream in = loader.getClassStream(oldName)) {
newCl = new StructClass(in, cl.isOwn(), loader);
newCl = StructClass.create(in, cl.isOwn(), loader);
}
lstClasses.add(newCl);
@ -92,6 +92,9 @@ public class ContextUnit {
// classes
for (int i = 0; i < classes.size(); i++) {
StructClass cl = classes.get(i);
if (!cl.isOwn()) {
continue;
}
String entryName = decompiledData.getClassEntryName(cl, classEntries.get(i));
if (entryName != null) {
String content = decompiledData.getClassContent(cl);

@ -1,7 +1,9 @@
// 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.
// Copyright 2000-2021 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.struct;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructRecordAttribute;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
import org.jetbrains.java.decompiler.struct.lazy.LazyLoader;
@ -10,6 +12,8 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/*
class_file {
@ -32,71 +36,86 @@ import java.io.IOException;
}
*/
public class StructClass extends StructMember {
public final String qualifiedName;
public final PrimitiveConstant superClass;
private final boolean own;
private final LazyLoader loader;
private final int minorVersion;
private final int majorVersion;
private final int[] interfaces;
private final String[] interfaceNames;
private final VBStyleCollection<StructField, String> fields;
private final VBStyleCollection<StructMethod, String> methods;
private ConstantPool pool;
public StructClass(byte[] bytes, boolean own, LazyLoader loader) throws IOException {
this(new DataInputFullStream(bytes), own, loader);
}
public StructClass(DataInputFullStream in, boolean own, LazyLoader loader) throws IOException {
this.own = own;
this.loader = loader;
public static StructClass create(DataInputFullStream in, boolean own, LazyLoader loader) throws IOException {
in.discard(4);
int minorVersion = in.readUnsignedShort();
int majorVersion = in.readUnsignedShort();
int bytecodeVersion = Math.max(majorVersion, CodeConstants.BYTECODE_JAVA_LE_4);
minorVersion = in.readUnsignedShort();
majorVersion = in.readUnsignedShort();
pool = new ConstantPool(in);
ConstantPool pool = new ConstantPool(in);
accessFlags = in.readUnsignedShort();
int accessFlags = in.readUnsignedShort();
int thisClassIdx = in.readUnsignedShort();
int superClassIdx = in.readUnsignedShort();
qualifiedName = pool.getPrimitiveConstant(thisClassIdx).getString();
superClass = pool.getPrimitiveConstant(superClassIdx);
String qualifiedName = pool.getPrimitiveConstant(thisClassIdx).getString();
PrimitiveConstant superClass = pool.getPrimitiveConstant(superClassIdx);
// interfaces
int length = in.readUnsignedShort();
interfaces = new int[length];
interfaceNames = new String[length];
int[] interfaces = new int[length];
String[] interfaceNames = new String[length];
for (int i = 0; i < length; i++) {
interfaces[i] = in.readUnsignedShort();
interfaceNames[i] = pool.getPrimitiveConstant(interfaces[i]).getString();
}
// fields
length = in.readUnsignedShort();
fields = new VBStyleCollection<>(length);
VBStyleCollection<StructField, String>fields = new VBStyleCollection<>(length);
for (int i = 0; i < length; i++) {
StructField field = new StructField(in, this);
StructField field = StructField.create(in, pool, qualifiedName);
fields.addWithKey(field, InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor()));
}
// methods
length = in.readUnsignedShort();
methods = new VBStyleCollection<>(length);
VBStyleCollection<StructMethod, String>methods = new VBStyleCollection<>(length);
for (int i = 0; i < length; i++) {
StructMethod method = new StructMethod(in, this);
StructMethod method = StructMethod.create(in, pool, qualifiedName, bytecodeVersion, own);
methods.addWithKey(method, InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor()));
}
// attributes
attributes = readAttributes(in, pool);
Map<String, StructGeneralAttribute> attributes = readAttributes(in, pool);
releaseResources();
StructClass cl = new StructClass(
accessFlags, attributes, qualifiedName, superClass, own, loader, minorVersion, majorVersion, interfaces, interfaceNames, fields, methods);
if (loader == null) cl.pool = pool;
return cl;
}
public final String qualifiedName;
public final PrimitiveConstant superClass;
private final boolean own;
private final LazyLoader loader;
private final int minorVersion;
private final int majorVersion;
private final int[] interfaces;
private final String[] interfaceNames;
private final VBStyleCollection<StructField, String> fields;
private final VBStyleCollection<StructMethod, String> methods;
private ConstantPool pool;
private StructClass(int accessFlags,
Map<String, StructGeneralAttribute> attributes,
String qualifiedName,
PrimitiveConstant superClass,
boolean own,
LazyLoader loader,
int minorVersion,
int majorVersion,
int[] interfaces,
String[] interfaceNames,
VBStyleCollection<StructField, String> fields,
VBStyleCollection<StructMethod, String> methods) {
super(accessFlags, attributes);
this.qualifiedName = qualifiedName;
this.superClass = superClass;
this.own = own;
this.loader = loader;
this.minorVersion = minorVersion;
this.majorVersion = majorVersion;
this.interfaces = interfaces;
this.interfaceNames = interfaceNames;
this.fields = fields;
this.methods = methods;
}
public boolean hasField(String name, String descriptor) {
@ -132,6 +151,15 @@ public class StructClass extends StructMember {
return pool;
}
/**
* @return list of record components; null if this class is not a record
*/
public List<StructRecordComponent> getRecordComponents() {
StructRecordAttribute recordAttr = getAttribute(StructGeneralAttribute.ATTRIBUTE_RECORD);
if (recordAttr == null) return null;
return recordAttr.getComponents();
}
public int[] getInterfaces() {
return interfaces;
}
@ -156,17 +184,13 @@ public class StructClass extends StructMember {
return loader;
}
public boolean isVersionGE_1_5() {
public boolean isVersion5() {
return (majorVersion > CodeConstants.BYTECODE_JAVA_LE_4 ||
(majorVersion == CodeConstants.BYTECODE_JAVA_LE_4 && minorVersion > 0)); // FIXME: check second condition
}
public boolean isVersionGE_1_7() {
return (majorVersion >= CodeConstants.BYTECODE_JAVA_7);
}
public int getBytecodeVersion() {
return Math.max(majorVersion, CodeConstants.BYTECODE_JAVA_LE_4);
public boolean isVersion8() {
return majorVersion >= CodeConstants.BYTECODE_JAVA_8;
}
@Override

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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.struct;
import org.jetbrains.java.decompiler.main.DecompilerContext;
@ -17,7 +17,6 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class StructContext {
private final IResultSaver saver;
private final IDecompiledData decompiledData;
private final LazyLoader loader;
@ -106,7 +105,7 @@ public class StructContext {
if (filename.endsWith(".class")) {
try (DataInputFullStream in = loader.getClassStream(file.getAbsolutePath(), null)) {
StructClass cl = new StructClass(in, isOwn, loader);
StructClass cl = StructClass.create(in, isOwn, loader);
classes.put(cl.qualifiedName, cl);
unit.addClass(cl, filename);
loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), null));
@ -141,7 +140,7 @@ public class StructContext {
if (!entry.isDirectory()) {
if (name.endsWith(".class")) {
byte[] bytes = InterpreterUtil.getBytes(archive, entry);
StructClass cl = new StructClass(bytes, isOwn, loader);
StructClass cl = StructClass.create(new DataInputFullStream(bytes), isOwn, loader);
classes.put(cl.qualifiedName, cl);
unit.addClass(cl, name);
loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), name));

@ -1,10 +1,12 @@
// 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.
// Copyright 2000-2021 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.struct;
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.util.DataInputFullStream;
import java.io.IOException;
import java.util.Map;
/*
field_info {
@ -16,29 +18,32 @@ import java.io.IOException;
}
*/
public class StructField extends StructMember {
public static StructField create(DataInputFullStream in, ConstantPool pool, String clQualifiedName) throws IOException {
int accessFlags = in.readUnsignedShort();
int nameIndex = in.readUnsignedShort();
int descriptorIndex = in.readUnsignedShort();
private final String name;
private final String descriptor;
String[] values = pool.getClassElement(ConstantPool.FIELD, clQualifiedName, nameIndex, descriptorIndex);
Map<String, StructGeneralAttribute> attributes = readAttributes(in, pool);
public StructField(DataInputFullStream in, StructClass clStruct) throws IOException {
accessFlags = in.readUnsignedShort();
int nameIndex = in.readUnsignedShort();
int descriptorIndex = in.readUnsignedShort();
return new StructField(accessFlags, attributes, values[0], values[1]);
}
ConstantPool pool = clStruct.getPool();
String[] values = pool.getClassElement(ConstantPool.FIELD, clStruct.qualifiedName, nameIndex, descriptorIndex);
name = values[0];
descriptor = values[1];
private final String name;
private final String descriptor;
attributes = readAttributes(in, pool);
protected StructField(int accessFlags, Map<String, StructGeneralAttribute> attributes, String name, String descriptor) {
super(accessFlags, attributes);
this.name = name;
this.descriptor = descriptor;
}
public String getName() {
public final String getName() {
return name;
}
public String getDescriptor() {
public final String getDescriptor() {
return descriptor;
}

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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.struct;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -12,23 +12,26 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class StructMember {
protected int accessFlags;
protected Map<String, StructGeneralAttribute> attributes;
public abstract class StructMember {
private final int accessFlags;
private final Map<String, StructGeneralAttribute> attributes;
protected StructMember(int accessFlags, Map<String, StructGeneralAttribute> attributes) {
this.accessFlags = accessFlags;
this.attributes = attributes;
}
public int getAccessFlags() {
return accessFlags;
}
public <T extends StructGeneralAttribute> T getAttribute(StructGeneralAttribute.Key<T> attribute) {
//noinspection unchecked
return (T)attributes.get(attribute.getName());
@SuppressWarnings("unchecked") T t = (T)attributes.get(attribute.name);
return t;
}
public boolean hasAttribute(StructGeneralAttribute.Key<?> attribute) {
return attributes.containsKey(attribute.getName());
return attributes.containsKey(attribute.name);
}
public boolean hasModifier(int modifier) {
@ -39,45 +42,37 @@ public class StructMember {
return hasModifier(CodeConstants.ACC_SYNTHETIC) || hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC);
}
protected Map<String, StructGeneralAttribute> readAttributes(DataInputFullStream in, ConstantPool pool) throws IOException {
public static Map<String, StructGeneralAttribute> readAttributes(DataInputFullStream in, ConstantPool pool) throws IOException {
int length = in.readUnsignedShort();
Map<String, StructGeneralAttribute> attributes = new HashMap<>(length);
for (int i = 0; i < length; i++) {
int nameIndex = in.readUnsignedShort();
String name = pool.getPrimitiveConstant(nameIndex).getString();
StructGeneralAttribute attribute = readAttribute(in, pool, name);
if (attribute != null) {
if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE.getName().equals(name) && attributes.containsKey(name)) {
StructGeneralAttribute attribute = StructGeneralAttribute.createAttribute(name);
int attLength = in.readInt();
if (attribute == null) {
in.discard(attLength);
}
else {
attribute.initContent(in, pool);
if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE.name.equals(name) && attributes.containsKey(name)) {
// merge all variable tables
StructLocalVariableTableAttribute table = (StructLocalVariableTableAttribute)attributes.get(name);
table.add((StructLocalVariableTableAttribute)attribute);
}
else if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.getName().equals(name) && attributes.containsKey(name)) {
else if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.name.equals(name) && attributes.containsKey(name)) {
// merge all variable tables
StructLocalVariableTypeTableAttribute table = (StructLocalVariableTypeTableAttribute)attributes.get(name);
table.add((StructLocalVariableTypeTableAttribute)attribute);
}
else {
attributes.put(attribute.getName(), attribute);
attributes.put(name, attribute);
}
}
}
return attributes;
}
protected StructGeneralAttribute readAttribute(DataInputFullStream in, ConstantPool pool, String name) throws IOException {
StructGeneralAttribute attribute = StructGeneralAttribute.createAttribute(name);
int length = in.readInt();
if (attribute == null) {
in.discard(length);
}
else {
attribute.initContent(in, pool);
}
return attribute;
}
}

@ -1,7 +1,8 @@
// 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.
// Copyright 2000-2021 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.struct;
import org.jetbrains.java.decompiler.code.*;
import org.jetbrains.java.decompiler.struct.attr.StructCodeAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
@ -25,80 +26,66 @@ import static org.jetbrains.java.decompiler.code.CodeConstants.*;
}
*/
public class StructMethod extends StructMember {
public static StructMethod create(DataInputFullStream in, ConstantPool pool, String clQualifiedName, int bytecodeVersion, boolean own) throws IOException {
int accessFlags = in.readUnsignedShort();
int nameIndex = in.readUnsignedShort();
int descriptorIndex = in.readUnsignedShort();
String[] values = pool.getClassElement(ConstantPool.METHOD, clQualifiedName, nameIndex, descriptorIndex);
Map<String, StructGeneralAttribute> attributes = readAttributes(in, pool);
StructCodeAttribute code = (StructCodeAttribute)attributes.remove(StructGeneralAttribute.ATTRIBUTE_CODE.name);
if (code != null) {
attributes.putAll(code.codeAttributes);
}
return new StructMethod(accessFlags, attributes, values[0], values[1], bytecodeVersion, own ? code : null);
}
private static final int[] opr_iconst = {-1, 0, 1, 2, 3, 4, 5};
private static final int[] opr_loadstore = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3};
private static final int[] opcs_load = {opc_iload, opc_lload, opc_fload, opc_dload, opc_aload};
private static final int[] opcs_store = {opc_istore, opc_lstore, opc_fstore, opc_dstore, opc_astore};
private final StructClass classStruct;
private final String name;
private final String descriptor;
private boolean containsCode = false;
private int localVariables = 0;
private int codeLength = 0;
private int codeFullLength = 0;
private InstructionSequence seq;
private final int bytecodeVersion;
private final int localVariables;
private final int codeLength;
private final int codeFullLength;
private InstructionSequence seq = null;
private boolean expanded = false;
private Map<String, StructGeneralAttribute> codeAttributes;
public StructMethod(DataInputFullStream in, StructClass clStruct) throws IOException {
classStruct = clStruct;
accessFlags = in.readUnsignedShort();
int nameIndex = in.readUnsignedShort();
int descriptorIndex = in.readUnsignedShort();
ConstantPool pool = clStruct.getPool();
String[] values = pool.getClassElement(ConstantPool.METHOD, clStruct.qualifiedName, nameIndex, descriptorIndex);
name = values[0];
descriptor = values[1];
attributes = readAttributes(in, pool);
if (codeAttributes != null) {
attributes.putAll(codeAttributes);
codeAttributes = null;
private StructMethod(int accessFlags,
Map<String, StructGeneralAttribute> attributes,
String name,
String descriptor,
int bytecodeVersion,
StructCodeAttribute code) {
super(accessFlags, attributes);
this.name = name;
this.descriptor = descriptor;
this.bytecodeVersion = bytecodeVersion;
if (code != null) {
this.localVariables = code.localVariables;
this.codeLength = code.codeLength;
this.codeFullLength = code.codeFullLength;
}
}
@Override
protected StructGeneralAttribute readAttribute(DataInputFullStream in, ConstantPool pool, String name) throws IOException {
if (StructGeneralAttribute.ATTRIBUTE_CODE.getName().equals(name)) {
if (!classStruct.isOwn()) {
// skip code in foreign classes
in.discard(8);
in.discard(in.readInt());
in.discard(8 * in.readUnsignedShort());
}
else {
containsCode = true;
in.discard(6);
localVariables = in.readUnsignedShort();
codeLength = in.readInt();
in.discard(codeLength);
int excLength = in.readUnsignedShort();
in.discard(excLength * 8);
codeFullLength = codeLength + excLength * 8 + 2;
}
codeAttributes = readAttributes(in, pool);
return null;
else {
this.localVariables = this.codeLength = this.codeFullLength = -1;
}
return super.readAttribute(in, pool, name);
}
public void expandData() throws IOException {
if (containsCode && !expanded) {
byte[] code = classStruct.getLoader().loadBytecode(this, codeFullLength);
public void expandData(StructClass classStruct) throws IOException {
if (codeLength >= 0 && !expanded) {
byte[] code = classStruct.getLoader().loadBytecode(classStruct, this, codeFullLength);
seq = parseBytecode(new DataInputFullStream(code), codeLength, classStruct.getPool());
expanded = true;
}
}
public void releaseResources() {
if (containsCode && expanded) {
if (codeLength >= 0 && expanded) {
seq = null;
expanded = false;
}
@ -108,10 +95,7 @@ public class StructMethod extends StructMember {
private InstructionSequence parseBytecode(DataInputFullStream in, int length, ConstantPool pool) throws IOException {
VBStyleCollection<Instruction, Integer> instructions = new VBStyleCollection<>();
int bytecode_version = classStruct.getBytecodeVersion();
for (int i = 0; i < length; ) {
int offset = i;
int opcode = in.readUnsignedByte();
@ -197,7 +181,7 @@ public class StructMethod extends StructMember {
}
break;
case opc_invokedynamic:
if (classStruct.isVersionGE_1_7()) { // instruction unused in Java 6 and before
if (bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) { // instruction unused in Java 6 and before
operands.add(in.readUnsignedShort());
in.discard(2);
group = GROUP_INVOCATION;
@ -313,7 +297,7 @@ public class StructMethod extends StructMember {
}
}
Instruction instr = Instruction.create(opcode, wide, group, bytecode_version, ops);
Instruction instr = Instruction.create(opcode, wide, group, bytecodeVersion, ops);
instructions.addWithKey(instr, offset);
@ -355,10 +339,6 @@ public class StructMethod extends StructMember {
return seq;
}
public StructClass getClassStruct() {
return classStruct;
}
public String getName() {
return name;
}
@ -367,8 +347,12 @@ public class StructMethod extends StructMember {
return descriptor;
}
public int getBytecodeVersion() {
return bytecodeVersion;
}
public boolean containsCode() {
return containsCode;
return codeLength >= 0;
}
public int getLocalVariables() {

@ -0,0 +1,36 @@
// Copyright 2000-2021 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.struct;
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
import org.jetbrains.java.decompiler.util.DataInputFullStream;
import java.io.IOException;
import java.util.Map;
/*
record_component_info {
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
*/
public class StructRecordComponent extends StructField {
public static StructRecordComponent create(DataInputFullStream in, ConstantPool pool) throws IOException {
int nameIndex = in.readUnsignedShort();
int descriptorIndex = in.readUnsignedShort();
String name = ((PrimitiveConstant)pool.getConstant(nameIndex)).getString();
String descriptor = ((PrimitiveConstant)pool.getConstant(descriptorIndex)).getString();
Map<String, StructGeneralAttribute> attributes = readAttributes(in, pool);
return new StructRecordComponent(0, attributes, name, descriptor);
}
private StructRecordComponent(int flags, Map<String, StructGeneralAttribute> attributes, String name, String descriptor) {
super(flags, attributes, name, descriptor);
}
}

@ -0,0 +1,43 @@
// Copyright 2000-2021 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.struct.attr;
import org.jetbrains.java.decompiler.struct.StructMember;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.util.DataInputFullStream;
import java.io.IOException;
import java.util.Map;
/*
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[];
u2 exception_table_length;
exception_table[] {
u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
};
u2 attributes_count;
attribute_info attributes[];
*/
public class StructCodeAttribute extends StructGeneralAttribute {
public int localVariables = 0;
public int codeLength = 0;
public int codeFullLength = 0;
public Map<String, StructGeneralAttribute> codeAttributes;
@Override
public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {
data.discard(2);
localVariables = data.readUnsignedShort();
codeLength = data.readInt();
data.discard(codeLength);
int excLength = data.readUnsignedShort();
data.discard(excLength * 8);
codeFullLength = codeLength + excLength * 8 + 2;
codeAttributes = StructMember.readAttributes(data, pool);
}
}

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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.struct.attr;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
@ -14,7 +14,7 @@ import java.io.IOException;
}
*/
public class StructGeneralAttribute {
public static final Key<StructGeneralAttribute> ATTRIBUTE_CODE = new Key<>("Code");
public static final Key<StructCodeAttribute> ATTRIBUTE_CODE = new Key<>("Code");
public static final Key<StructInnerClassesAttribute> ATTRIBUTE_INNER_CLASSES = new Key<>("InnerClasses");
public static final Key<StructGenericSignatureAttribute> ATTRIBUTE_SIGNATURE = new Key<>("Signature");
public static final Key<StructAnnDefaultAttribute> ATTRIBUTE_ANNOTATION_DEFAULT = new Key<>("AnnotationDefault");
@ -34,85 +34,81 @@ public class StructGeneralAttribute {
public static final Key<StructGeneralAttribute> ATTRIBUTE_DEPRECATED = new Key<>("Deprecated");
public static final Key<StructLineNumberTableAttribute> ATTRIBUTE_LINE_NUMBER_TABLE = new Key<>("LineNumberTable");
public static final Key<StructMethodParametersAttribute> ATTRIBUTE_METHOD_PARAMETERS = new Key<>("MethodParameters");
public static final Key<StructModuleAttribute> ATTRIBUTE_MODULE = new Key<>("Module");
public static final Key<StructRecordAttribute> ATTRIBUTE_RECORD = new Key<>("Record");
public static final Key<StructOriginalPcTableAttribute> ATTRIBUTE_ORIGINAL_PC_TABLE = new Key<>("OriginalPcTable");
@SuppressWarnings("unused")
public static class Key<T extends StructGeneralAttribute> {
private final String name;
public final String name;
public Key(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
private String name;
public static StructGeneralAttribute createAttribute(String name) {
StructGeneralAttribute attr;
if (ATTRIBUTE_INNER_CLASSES.getName().equals(name)) {
attr = new StructInnerClassesAttribute();
if (ATTRIBUTE_CODE.name.equals(name)) {
return new StructCodeAttribute();
}
else if (ATTRIBUTE_INNER_CLASSES.name.equals(name)) {
return new StructInnerClassesAttribute();
}
else if (ATTRIBUTE_CONSTANT_VALUE.getName().equals(name)) {
attr = new StructConstantValueAttribute();
else if (ATTRIBUTE_CONSTANT_VALUE.name.equals(name)) {
return new StructConstantValueAttribute();
}
else if (ATTRIBUTE_SIGNATURE.getName().equals(name)) {
attr = new StructGenericSignatureAttribute();
else if (ATTRIBUTE_SIGNATURE.name.equals(name)) {
return new StructGenericSignatureAttribute();
}
else if (ATTRIBUTE_ANNOTATION_DEFAULT.getName().equals(name)) {
attr = new StructAnnDefaultAttribute();
else if (ATTRIBUTE_ANNOTATION_DEFAULT.name.equals(name)) {
return new StructAnnDefaultAttribute();
}
else if (ATTRIBUTE_EXCEPTIONS.getName().equals(name)) {
attr = new StructExceptionsAttribute();
else if (ATTRIBUTE_EXCEPTIONS.name.equals(name)) {
return new StructExceptionsAttribute();
}
else if (ATTRIBUTE_ENCLOSING_METHOD.getName().equals(name)) {
attr = new StructEnclosingMethodAttribute();
else if (ATTRIBUTE_ENCLOSING_METHOD.name.equals(name)) {
return new StructEnclosingMethodAttribute();
}
else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.getName().equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.getName().equals(name)) {
attr = new StructAnnotationAttribute();
else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.name.equals(name)) {
return new StructAnnotationAttribute();
}
else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.getName().equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.getName().equals(name)) {
attr = new StructAnnotationParameterAttribute();
else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.name.equals(name)) {
return new StructAnnotationParameterAttribute();
}
else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.getName().equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.getName().equals(name)) {
attr = new StructTypeAnnotationAttribute();
else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.name.equals(name)) {
return new StructTypeAnnotationAttribute();
}
else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.getName().equals(name)) {
attr = new StructLocalVariableTableAttribute();
else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.name.equals(name)) {
return new StructLocalVariableTableAttribute();
}
else if (ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.getName().equals(name)) {
attr = new StructLocalVariableTypeTableAttribute();
else if (ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.name.equals(name)) {
return new StructLocalVariableTypeTableAttribute();
}
else if (ATTRIBUTE_BOOTSTRAP_METHODS.getName().equals(name)) {
attr = new StructBootstrapMethodsAttribute();
else if (ATTRIBUTE_BOOTSTRAP_METHODS.name.equals(name)) {
return new StructBootstrapMethodsAttribute();
}
else if (ATTRIBUTE_SYNTHETIC.getName().equals(name) || ATTRIBUTE_DEPRECATED.getName().equals(name)) {
attr = new StructGeneralAttribute();
else if (ATTRIBUTE_SYNTHETIC.name.equals(name) || ATTRIBUTE_DEPRECATED.name.equals(name)) {
return new StructGeneralAttribute();
}
else if (ATTRIBUTE_LINE_NUMBER_TABLE.getName().equals(name)) {
attr = new StructLineNumberTableAttribute();
else if (ATTRIBUTE_LINE_NUMBER_TABLE.name.equals(name)) {
return new StructLineNumberTableAttribute();
}
else if (ATTRIBUTE_METHOD_PARAMETERS.getName().equals(name)) {
attr = new StructMethodParametersAttribute();
else if (ATTRIBUTE_METHOD_PARAMETERS.name.equals(name)) {
return new StructMethodParametersAttribute();
}
else if (ATTRIBUTE_ORIGINAL_PC_TABLE.getName().equals(name)) {
attr = new StructOriginalPcTableAttribute();
else if (ATTRIBUTE_MODULE.name.equals(name)) {
return new StructModuleAttribute();
}
else if (ATTRIBUTE_RECORD.name.equals(name)) {
return new StructRecordAttribute();
}
else if (ATTRIBUTE_ORIGINAL_PC_TABLE.name.equals(name)) {
return new StructOriginalPcTableAttribute();
}
else {
// unsupported attribute
return null;
return null; // unsupported attribute
}
attr.name = name;
return attr;
}
public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { }
public String getName() {
return name;
}
}

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.struct.attr;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
@ -10,7 +10,7 @@ import java.util.Collections;
import java.util.List;
public class StructInnerClassesAttribute extends StructGeneralAttribute {
public static class Entry {
public static final class Entry {
public final int outerNameIdx;
public final int simpleNameIdx;
public final int accessFlags;

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.struct.attr;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
@ -71,7 +71,7 @@ public class StructLocalVariableTableAttribute extends StructGeneralAttribute {
return localVariables.stream().filter(v -> v.start_pc == 0).collect(Collectors.toMap(v -> v.index, v -> v.name, (n1, n2) -> n2));
}
private static class LocalVariable {
private static final class LocalVariable {
final int start_pc;
final int length;
final String name;

@ -0,0 +1,165 @@
// Copyright 2000-2021 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.struct.attr;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.util.DataInputFullStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class StructModuleAttribute extends StructGeneralAttribute {
public String moduleName;
public int moduleFlags;
public String moduleVersion;
public List<RequiresEntry> requires;
public List<ExportsEntry> exports;
public List<OpensEntry> opens;
public List<String> uses;
public List<ProvidesEntry> provides;
@Override
public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {
int moduleNameIndex = data.readUnsignedShort();
this.moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString();
this.moduleFlags = data.readUnsignedShort();
int moduleVersionIndex = data.readUnsignedShort();
if (moduleVersionIndex != 0) {
moduleVersion = pool.getPrimitiveConstant(moduleVersionIndex).getString();
}
this.requires = readRequires(data, pool);
this.exports = readExports(data, pool);
this.opens = readOpens(data, pool);
this.uses = readUses(data, pool);
this.provides = readProvides(data, pool);
}
public List<RequiresEntry> readRequires(DataInputFullStream data, ConstantPool pool) throws IOException {
int requiresCount = data.readUnsignedShort();
if (requiresCount <= 0) return Collections.emptyList();
List<RequiresEntry> requires = new ArrayList<>(requiresCount);
for (int i = 0; i < requiresCount; i++) {
int moduleNameIndex = data.readUnsignedShort();
int requiresFlags = data.readUnsignedShort();
int versionIndex = data.readUnsignedShort();
String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString();
String version = versionIndex == 0 ? null : pool.getPrimitiveConstant(versionIndex).getString();
requires.add(new RequiresEntry(moduleName, requiresFlags, version));
}
return requires;
}
private static List<ExportsEntry> readExports(DataInputFullStream data, ConstantPool pool) throws IOException {
int exportsCount = data.readUnsignedShort();
if (exportsCount <= 0) return Collections.emptyList();
List<ExportsEntry> exports = new ArrayList<>(exportsCount);
for (int i = 0; i < exportsCount; i++) {
int packageNameIndex = data.readUnsignedShort();
int exportsFlags = data.readUnsignedShort();
List<String> exportsToModules = readStringList(data, pool);
String packageName = pool.getPrimitiveConstant(packageNameIndex).getString();
exports.add(new ExportsEntry(packageName, exportsFlags, exportsToModules));
}
return exports;
}
private static List<OpensEntry> readOpens(DataInputFullStream data, ConstantPool pool) throws IOException {
int opensCount = data.readUnsignedShort();
if (opensCount <= 0) return Collections.emptyList();
List<OpensEntry> opens = new ArrayList<>(opensCount);
for (int i = 0; i < opensCount; i++) {
int packageNameIndex = data.readUnsignedShort();
int opensFlags = data.readUnsignedShort();
List<String> opensToModules = readStringList(data, pool);
String packageName = pool.getPrimitiveConstant(packageNameIndex).getString();
opens.add(new OpensEntry(packageName, opensFlags, opensToModules));
}
return opens;
}
private static List<String> readUses(DataInputFullStream data, ConstantPool pool) throws IOException {
return readStringList(data, pool);
}
private static List<ProvidesEntry> readProvides(DataInputFullStream data, ConstantPool pool) throws IOException {
int providesCount = data.readUnsignedShort();
if (providesCount <= 0) return Collections.emptyList();
List<ProvidesEntry> provides = new ArrayList<>(providesCount);
for (int i = 0; i < providesCount; i++) {
int interfaceNameIndex = data.readUnsignedShort();
String interfaceName = pool.getPrimitiveConstant(interfaceNameIndex).getString();
List<String> implementationNames = readStringList(data, pool);
provides.add(new ProvidesEntry(interfaceName, implementationNames));
}
return provides;
}
private static List<String> readStringList(DataInputFullStream data, ConstantPool pool) throws IOException {
int count = data.readUnsignedShort();
if (count <= 0) {
return Collections.emptyList();
}
else {
List<String> strings = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
int index = data.readUnsignedShort();
strings.add(pool.getPrimitiveConstant(index).getString());
}
return strings;
}
}
public static final class RequiresEntry {
public final String moduleName;
public final int flags;
public final String moduleVersion;
public RequiresEntry(String moduleName, int flags, String moduleVersion) {
this.moduleName = moduleName;
this.flags = flags;
this.moduleVersion = moduleVersion;
}
}
public static final class ExportsEntry {
public final String packageName;
public final int flags;
public final List<String> exportToModules;
public ExportsEntry(String packageName, int flags, List<String> exportToModules) {
this.packageName = packageName;
this.flags = flags;
this.exportToModules = exportToModules;
}
}
public static final class OpensEntry {
public final String packageName;
public final int flags;
public final List<String> opensToModules;
public OpensEntry(String packageName, int flags, List<String> exportToModules) {
this.packageName = packageName;
this.flags = flags;
this.opensToModules = exportToModules;
}
}
public static final class ProvidesEntry {
public final String interfaceName;
public final List<String> implementationNames;
public ProvidesEntry(String interfaceName, List<String> implementationNames) {
this.interfaceName = interfaceName;
this.implementationNames = implementationNames;
}
}
}

@ -0,0 +1,37 @@
// Copyright 2000-2021 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.struct.attr;
import org.jetbrains.java.decompiler.struct.StructRecordComponent;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.util.DataInputFullStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/*
Record_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 components_count;
record_component_info components[components_count];
}
*/
public class StructRecordAttribute extends StructGeneralAttribute {
List<StructRecordComponent> components;
@Override
public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {
int componentCount = data.readUnsignedShort();
StructRecordComponent[] components = new StructRecordComponent[componentCount];
for (int i = 0; i < componentCount; i++) {
components[i] = StructRecordComponent.create(data, pool);
}
this.components = Arrays.asList(components);
}
public List<StructRecordComponent> getComponents() {
return Collections.unmodifiableList(components);
}
}

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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.struct.consts;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -64,6 +64,8 @@ public class ConstantPool implements NewClassNameBuilder {
case CodeConstants.CONSTANT_Class:
case CodeConstants.CONSTANT_String:
case CodeConstants.CONSTANT_MethodType:
case CodeConstants.CONSTANT_Module:
case CodeConstants.CONSTANT_Package:
pool.add(new PrimitiveConstant(tag, in.readUnsignedShort()));
nextPass[0].set(i);
break;
@ -210,18 +212,12 @@ public class ConstantPool implements NewClassNameBuilder {
String newName = interceptor.getName(vt.value);
if (newName != null) {
StringBuilder buffer = new StringBuilder();
if (vt.arrayDim > 0) {
for (int i = 0; i < vt.arrayDim; i++) {
buffer.append('[');
}
buffer.append('L').append(newName).append(';');
buffer.append("[".repeat(vt.arrayDim)).append('L').append(newName).append(';');
}
else {
buffer.append(newName);
}
return buffer.toString();
}

@ -26,10 +26,11 @@ public class LinkConstant extends PooledConstant {
if (type == CONSTANT_Methodref ||
type == CONSTANT_InterfaceMethodref ||
type == CONSTANT_InvokeDynamic ||
type == CONSTANT_MethodHandle) {
(type == CONSTANT_MethodHandle && index1 != CONSTANT_MethodHandle_REF_getField && index1 != CONSTANT_MethodHandle_REF_putField)) {
int parenth = descriptor.indexOf(')');
if (descriptor.length() < 2 || parenth < 0 || descriptor.charAt(0) != '(') {
throw new IllegalArgumentException("Invalid descriptor: " + descriptor);
throw new IllegalArgumentException("Invalid descriptor: " + descriptor +
"; type = " + type + "; classname = " + classname + "; elementname = " + elementname);
}
}
}

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 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.struct.consts;
public class PrimitiveConstant extends PooledConstant {
@ -31,7 +31,7 @@ public class PrimitiveConstant extends PooledConstant {
@Override
public void resolveConstant(ConstantPool pool) {
if (type == CONSTANT_Class || type == CONSTANT_String || type == CONSTANT_MethodType) {
if (type == CONSTANT_Class || type == CONSTANT_String || type == CONSTANT_MethodType || type == CONSTANT_Module || type == CONSTANT_Package) {
value = pool.getPrimitiveConstant(index).getString();
initConstant();
}

@ -1,9 +1,9 @@
// 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.
// Copyright 2000-2020 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.struct.gen;
import org.jetbrains.java.decompiler.code.CodeConstants;
public class FieldDescriptor {
public final class FieldDescriptor {
public static final FieldDescriptor INTEGER_DESCRIPTOR = parseDescriptor("Ljava/lang/Integer;");
public static final FieldDescriptor LONG_DESCRIPTOR = parseDescriptor("Ljava/lang/Long;");

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.struct.gen;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -7,7 +7,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MethodDescriptor {
public final class MethodDescriptor {
public final VarType[] params;
public final VarType ret;
@ -68,8 +68,7 @@ public class MethodDescriptor {
VarType[] newParams;
if (params.length > 0) {
newParams = new VarType[params.length];
System.arraycopy(params, 0, newParams, 0, params.length);
newParams = params.clone();
for (int i = 0; i < params.length; i++) {
VarType substitute = buildNewType(params[i], builder);
if (substitute != null) {

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.struct.gen.generics;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -9,7 +9,7 @@ import org.jetbrains.java.decompiler.util.TextUtil;
import java.util.ArrayList;
import java.util.List;
public class GenericMain {
public final class GenericMain {
private static final String[] typeNames = {
"byte",

@ -1,7 +1,8 @@
// 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.
// Copyright 2000-2021 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.struct.lazy;
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
@ -19,20 +20,20 @@ public class LazyLoader {
this.provider = provider;
}
public void addClassLink(String classname, Link link) {
mapClassLinks.put(classname, link);
public void addClassLink(String className, Link link) {
mapClassLinks.put(className, link);
}
public void removeClassLink(String classname) {
mapClassLinks.remove(classname);
public void removeClassLink(String className) {
mapClassLinks.remove(className);
}
public Link getClassLink(String classname) {
return mapClassLinks.get(classname);
public Link getClassLink(String className) {
return mapClassLinks.get(className);
}
public ConstantPool loadPool(String classname) {
try (DataInputFullStream in = getClassStream(classname)) {
public ConstantPool loadPool(String className) {
try (DataInputFullStream in = getClassStream(className)) {
if (in != null) {
in.discard(8);
return new ConstantPool(in);
@ -45,14 +46,14 @@ public class LazyLoader {
}
}
public byte[] loadBytecode(StructMethod mt, int codeFullLength) {
String className = mt.getClassStruct().qualifiedName;
public byte[] loadBytecode(StructClass classStruct, StructMethod mt, int codeFullLength) {
String className = classStruct.qualifiedName;
try (DataInputFullStream in = getClassStream(className)) {
if (in != null) {
in.discard(8);
ConstantPool pool = mt.getClassStruct().getPool();
ConstantPool pool = classStruct.getPool();
if (pool == null) {
pool = new ConstantPool(in);
}
@ -90,7 +91,7 @@ public class LazyLoader {
for (int j = 0; j < attrSize; j++) {
int attrNameIndex = in.readUnsignedShort();
String attrName = pool.getPrimitiveConstant(attrNameIndex).getString();
if (!StructGeneralAttribute.ATTRIBUTE_CODE.getName().equals(attrName)) {
if (!StructGeneralAttribute.ATTRIBUTE_CODE.name.equals(attrName)) {
in.discard(in.readInt());
continue;
}

@ -1,10 +1,7 @@
// 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.
// Copyright 2000-2020 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.util;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.*;
public class FastFixedSetFactory<E> {
@ -46,7 +43,7 @@ public class FastFixedSetFactory<E> {
return colValuesInternal;
}
public static class FastFixedSet<E> implements Iterable<E> {
public static final class FastFixedSet<E> implements Iterable<E> {
private final FastFixedSetFactory<E> factory;
@ -66,8 +63,7 @@ public class FastFixedSetFactory<E> {
FastFixedSet<E> copy = new FastFixedSet<>(factory);
int arrlength = data.length;
int[] cpdata = new int[arrlength];
System.arraycopy(data, 0, cpdata, 0, arrlength);
int[] cpdata = Arrays.copyOf(data, arrlength);
copy.setData(cpdata);
return copy;
@ -242,7 +238,7 @@ public class FastFixedSetFactory<E> {
}
}
public static class FastFixedSetIterator<E> implements Iterator<E> {
public static final class FastFixedSetIterator<E> implements Iterator<E> {
private final VBStyleCollection<int[], E> colValuesInternal;
private final int[] data;

@ -1,10 +1,7 @@
// 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.
// Copyright 2000-2020 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.util;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.*;
public class FastSparseSetFactory<E> {
@ -69,7 +66,7 @@ public class FastSparseSetFactory<E> {
}
public static class FastSparseSet<E> implements Iterable<E> {
public static final class FastSparseSet<E> implements Iterable<E> {
public static final FastSparseSet[] EMPTY_ARRAY = new FastSparseSet[0];
private final FastSparseSetFactory<E> factory;
@ -97,15 +94,7 @@ public class FastSparseSetFactory<E> {
}
public FastSparseSet<E> getCopy() {
int arrlength = data.length;
int[] cpdata = new int[arrlength];
int[] cpnext = new int[arrlength];
System.arraycopy(data, 0, cpdata, 0, arrlength);
System.arraycopy(next, 0, cpnext, 0, arrlength);
return new FastSparseSet<>(factory, cpdata, cpnext);
return new FastSparseSet<>(factory, data.clone(), next.clone());
}
private int[] ensureCapacity(int index) {
@ -119,15 +108,10 @@ public class FastSparseSetFactory<E> {
newlength *= 2;
}
int[] newdata = new int[newlength];
System.arraycopy(data, 0, newdata, 0, data.length);
data = newdata;
data = Arrays.copyOf(data, newlength);
next = Arrays.copyOf(next, newlength);
int[] newnext = new int[newlength];
System.arraycopy(next, 0, newnext, 0, next.length);
next = newnext;
return newdata;
return data;
}
public void add(E element) {
@ -360,7 +344,7 @@ public class FastSparseSetFactory<E> {
}
}
public static class FastSparseSetIterator<E> implements Iterator<E> {
public static final class FastSparseSetIterator<E> implements Iterator<E> {
private final VBStyleCollection<int[], E> colValuesInternal;
private final int[] data;

@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2020 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.util;
import java.io.*;
@ -8,7 +8,7 @@ import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class InterpreterUtil {
public final class InterpreterUtil {
public static final boolean IS_WINDOWS = System.getProperty("os.name", "").startsWith("Windows");
public static final int[] EMPTY_INT_ARRAY = new int[0];

@ -5,6 +5,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
@ -66,9 +67,7 @@ public class SFormsFastMapDirect {
int[] arrnext = next[i];
@SuppressWarnings("unchecked") FastSparseSet<Integer>[] arrnew = new FastSparseSet[length];
int[] arrnextnew = new int[length];
System.arraycopy(arrnext, 0, arrnextnew, 0, length);
int[] arrnextnew = Arrays.copyOf(arrnext, length);
mapelements[i] = arrnew;
mapnext[i] = arrnextnew;
@ -343,7 +342,7 @@ public class SFormsFastMapDirect {
if (ent != null) {
final int key = i == 0 ? ikey : (i == 1 ? ikey + VarExprent.STACK_BASE : -ikey);
list.add(new Entry<Integer, FastSparseSet<Integer>>() {
list.add(new Entry<>() {
private final Integer var = key;
private final FastSparseSet<Integer> val = ent;

@ -1,6 +1,4 @@
/*
* 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.
*/
// Copyright 2000-2020 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.util;
import org.jetbrains.java.decompiler.code.CodeConstants;
@ -12,7 +10,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
import java.util.Arrays;
import java.util.HashSet;
public class TextUtil {
public final class TextUtil {
private static final HashSet<String> KEYWORDS = new HashSet<>(Arrays.asList(
"abstract", "default", "if", "private", "this", "boolean", "do", "implements", "protected", "throw", "break", "double", "import",
"public", "throws", "byte", "else", "instanceof", "return", "transient", "case", "extends", "int", "short", "try", "catch", "final",

@ -1,11 +1,13 @@
// 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.
// Copyright 2000-2021 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;
import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import java.io.File;
import java.io.IOException;
@ -19,6 +21,13 @@ import static org.junit.Assert.assertTrue;
public class SingleClassesTest {
private DecompilerTestFixture fixture;
/*
* Set individual test duration time limit to 60 seconds.
* This will help us to test bugs hanging decompiler.
*/
@Rule
public Timeout globalTimeout = Timeout.seconds(60);
@Before
public void setUp() throws IOException {
fixture = new DecompilerTestFixture();
@ -70,10 +79,10 @@ public class SingleClassesTest {
@Test public void testAnonymousSignature() { doTest("pkg/TestAnonymousSignature"); }
@Test public void testLocalsSignature() { doTest("pkg/TestLocalsSignature"); }
@Test public void testParameterizedTypes() { doTest("pkg/TestParameterizedTypes"); }
@Test public void testShadowing() { doTest("pkg/TestShadowing", "pkg/Shadow", "ext/Shadow",
"pkg/TestShadowingSuperClass"); }
@Test public void testShadowing() { doTest("pkg/TestShadowing", "pkg/Shadow", "ext/Shadow", "pkg/TestShadowingSuperClass"); }
@Test public void testStringConcat() { doTest("pkg/TestStringConcat"); }
@Test public void testJava9StringConcat() { doTest("java9/TestJava9StringConcat"); }
@Test public void testJava9ModuleInfo() { doTest("java9/module-info"); }
@Test public void testJava11StringConcat() { doTest("java11/TestJava11StringConcat"); }
@Test public void testMethodReferenceSameName() { doTest("pkg/TestMethodReferenceSameName"); }
@Test public void testMethodReferenceLetterClass() { doTest("pkg/TestMethodReferenceLetterClass"); }
@ -110,13 +119,14 @@ public class SingleClassesTest {
@Test public void testMissingConstructorCallGood() { doTest("pkg/TestMissingConstructorCallGood"); }
@Test public void testMissingConstructorCallBad() { doTest("pkg/TestMissingConstructorCallBad"); }
@Test public void testEmptyBlocks() { doTest("pkg/TestEmptyBlocks"); }
@Test public void testInvertedFloatComparison() { doTest("pkg/TestInvertedFloatComparison"); }
@Test public void testPrivateEmptyConstructor() { doTest("pkg/TestPrivateEmptyConstructor"); }
@Test public void testSynchronizedUnprotected() { doTest("pkg/TestSynchronizedUnprotected"); }
@Test public void testInterfaceSuper() { doTest("pkg/TestInterfaceSuper"); }
@Test public void testFieldSingleAccess() { doTest("pkg/TestFieldSingleAccess"); }
@Test public void testPackageInfo() { doTest("pkg/package-info"); }
// TODO: fix all below
//@Test public void testPackageInfo() { doTest("pkg/package-info"); }
//@Test public void testSwitchOnStrings() { doTest("pkg/TestSwitchOnStrings");}
//@Test public void testUnionType() { doTest("pkg/TestUnionType"); }
//@Test public void testInnerClassConstructor2() { doTest("pkg/TestInner2"); }
@ -128,6 +138,13 @@ public class SingleClassesTest {
@Test public void testSuspendLambda() { doTest("pkg/TestSuspendLambdaKt"); }
@Test public void testNamedSuspendFun2Kt() { doTest("pkg/TestNamedSuspendFun2Kt"); }
@Test public void testGenericArgs() { doTest("pkg/TestGenericArgs"); }
@Test public void testRecordEmpty() { doTest("records/TestRecordEmpty"); }
@Test public void testRecordSimple() { doTest("records/TestRecordSimple"); }
@Test public void testRecordVararg() { doTest("records/TestRecordVararg"); }
@Test public void testRecordGenericVararg() { doTest("records/TestRecordGenericVararg"); }
@Test public void testRecordAnno() { doTest("records/TestRecordAnno"); }
@Test public void testInheritanceChainCycle() { doTest("pkg/TestInheritanceChainCycle"); }
private void doTest(String testFile, String... companionFiles) {
ConsoleDecompiler decompiler = fixture.getDecompiler();

@ -0,0 +1,22 @@
package pkg;
public class TestInheritanceChainCycle extends TestInheritanceChainCycle {
public void printMessage() {
System.out.println("Hello, bug!");// 21 22 23
}// 24
}
class 'pkg/TestInheritanceChainCycle' {
method 'printMessage ()V' {
0 4
3 4
5 4
8 5
}
}
Lines mapping:
21 <-> 5
22 <-> 5
23 <-> 5
24 <-> 6

@ -0,0 +1,167 @@
package pkg;
public class TestInvertedFloatComparison {
public boolean less(double var1, double var3) {
return var1 < var3;// 6
}
public boolean less(int var1, int var2) {
return var1 < var2;// 10
}
public boolean notLess(double var1, double var3) {
return !(var1 < var3);// 14
}
public boolean notLess(int var1, int var2) {
return var1 >= var2;// 18
}
public boolean greater(double var1, double var3) {
return var1 > var3;// 22
}
public boolean greater(int var1, int var2) {
return var1 > var2;// 26
}
public boolean notGreater(double var1, double var3) {
return !(var1 > var3);// 30
}
public boolean notGreater(int var1, int var2) {
return var1 <= var2;// 34
}
public boolean lessEqual(double var1, double var3) {
return var1 <= var3;// 38
}
public boolean lessEqual(int var1, int var2) {
return var1 <= var2;// 42
}
public boolean notLessEqual(double var1, double var3) {
return !(var1 <= var3);// 46
}
public boolean notLessEqual(int var1, int var2) {
return var1 > var2;// 50
}
public boolean greaterEqual(double var1, double var3) {
return var1 >= var3;// 54
}
public boolean greaterEqual(int var1, int var2) {
return var1 >= var2;// 58
}
public boolean notGreaterEqual(double var1, double var3) {
return !(var1 >= var3);// 62
}
public boolean notGreaterEqual(int var1, int var2) {
return var1 < var2;// 66
}
}
class 'pkg/TestInvertedFloatComparison' {
method 'less (DD)Z' {
2 4
b 4
}
method 'less (II)Z' {
2 8
a 8
}
method 'notLess (DD)Z' {
2 12
b 12
}
method 'notLess (II)Z' {
2 16
a 16
}
method 'greater (DD)Z' {
2 20
b 20
}
method 'greater (II)Z' {
2 24
a 24
}
method 'notGreater (DD)Z' {
2 28
b 28
}
method 'notGreater (II)Z' {
2 32
a 32
}
method 'lessEqual (DD)Z' {
2 36
b 36
}
method 'lessEqual (II)Z' {
2 40
a 40
}
method 'notLessEqual (DD)Z' {
2 44
b 44
}
method 'notLessEqual (II)Z' {
2 48
a 48
}
method 'greaterEqual (DD)Z' {
2 52
b 52
}
method 'greaterEqual (II)Z' {
2 56
a 56
}
method 'notGreaterEqual (DD)Z' {
2 60
b 60
}
method 'notGreaterEqual (II)Z' {
2 64
a 64
}
}
Lines mapping:
6 <-> 5
10 <-> 9
14 <-> 13
18 <-> 17
22 <-> 21
26 <-> 25
30 <-> 29
34 <-> 33
38 <-> 37
42 <-> 41
46 <-> 45
50 <-> 49
54 <-> 53
58 <-> 57
62 <-> 61
66 <-> 65

@ -0,0 +1,39 @@
package records;
public record TestRecordAnno(@RC @TA int x, int y) {
public TestRecordAnno(@TA int x, @P int y) {
this.x = x;
this.y = y;
}
@TA
public int x() {
return this.x;
}
@M
public int y() {
return this.y;// 5
}
}
class 'records/TestRecordAnno' {
method '<init> (II)V' {
6 4
b 5
e 6
}
method 'x ()I' {
1 10
4 10
}
method 'y ()I' {
1 15
4 15
}
}
Lines mapping:
5 <-> 16

@ -0,0 +1,17 @@
package records;
public record TestRecordEmpty() {
public int hashCode() {
return 0;// 5
}
}
class 'records/TestRecordEmpty' {
method 'hashCode ()I' {
0 4
1 4
}
}
Lines mapping:
5 <-> 5

@ -0,0 +1,39 @@
package records;
public record TestRecordGenericVararg<T>(T first, T... other) {
@SafeVarargs
public TestRecordGenericVararg(T first, T... other) {
this.first = first;// 5
this.other = other;
}
public T first() {
return this.first;
}
public T[] other() {
return this.other;// 3
}
}
class 'records/TestRecordGenericVararg' {
method '<init> (Ljava/lang/Object;[Ljava/lang/Object;)V' {
6 5
b 6
e 7
}
method 'first ()Ljava/lang/Object;' {
1 10
4 10
}
method 'other ()[Ljava/lang/Object;' {
1 14
4 14
}
}
Lines mapping:
3 <-> 15
5 <-> 6

@ -0,0 +1,37 @@
package records;
public record TestRecordSimple(int x, int y) {
public TestRecordSimple(int x, int y) {
this.x = x;
this.y = y;
}
public int x() {
return this.x;
}
public int y() {
return this.y;// 3
}
}
class 'records/TestRecordSimple' {
method '<init> (II)V' {
6 4
b 5
e 6
}
method 'x ()I' {
1 9
4 9
}
method 'y ()I' {
1 13
4 13
}
}
Lines mapping:
3 <-> 14

@ -0,0 +1,37 @@
package records;
public record TestRecordVararg(int x, int[]... y) {
public TestRecordVararg(int x, int[]... y) {
this.x = x;
this.y = y;
}
public int x() {
return this.x;
}
public int[][] y() {
return this.y;// 3
}
}
class 'records/TestRecordVararg' {
method '<init> (I[[I)V' {
6 4
b 5
e 6
}
method 'x ()I' {
1 9
4 9
}
method 'y ()[[I' {
1 13
4 13
}
}
Lines mapping:
3 <-> 14

@ -0,0 +1,19 @@
import sample.pkg1.TestModuleAnno;
@TestModuleAnno("...")
module sample.module {
requires java.desktop;
exports sample.pkg1;
exports sample.pkg2 to
java.base;
opens sample.pkg1;
opens sample.pkg2 to
java.base;
uses java.util.spi.ToolProvider;
provides sample.pkg1.TestService with
sample.pkg1.TestServiceImpl;
}

@ -1,2 +1,4 @@
@jdk.Exported
@PkgAnno("...")
package pkg;
import ext.PkgAnno;

@ -0,0 +1,8 @@
package ext;
import java.lang.annotation.*;
@Target(ElementType.PACKAGE)
public @interface PkgAnno {
String value();
}

@ -0,0 +1,3 @@
package sample.pkg2;
public class TestClass {}

@ -0,0 +1,8 @@
package sample.pkg1;
import java.lang.annotation.*;
@Target(ElementType.MODULE)
public @interface TestModuleAnno {
String value();
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save