diff --git a/build.gradle b/build.gradle index 9694117..d158b9c 100644 --- a/build.gradle +++ b/build.gradle @@ -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,15 +11,15 @@ 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' } -} \ No newline at end of file +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index cd13592..e708b1c 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 657de85..f371643 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/gradlew b/gradlew index cccdd3d..4f906e0 100755 --- a/gradlew +++ b/gradlew @@ -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" "$@" diff --git a/gradlew.bat b/gradlew.bat index f955316..107acd3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -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 diff --git a/src/org/jetbrains/java/decompiler/code/CodeConstants.java b/src/org/jetbrains/java/decompiler/code/CodeConstants.java index 720d2a7..2c5cb42 100644 --- a/src/org/jetbrains/java/decompiler/code/CodeConstants.java +++ b/src/org/jetbrains/java/decompiler/code/CodeConstants.java @@ -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 diff --git a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java index 5f9068a..50a6d87 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java @@ -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 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) { diff --git a/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java b/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java index 3c0abe4..d388659 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java @@ -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); diff --git a/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java b/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java index 5c56593..aeafe5c 100644 --- a/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java +++ b/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java @@ -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 = { diff --git a/src/org/jetbrains/java/decompiler/main/AssertProcessor.java b/src/org/jetbrains/java/decompiler/main/AssertProcessor.java index b0e7b71..a8863c5 100644 --- a/src/org/jetbrains/java/decompiler/main/AssertProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/AssertProcessor.java @@ -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"); diff --git a/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java b/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java index 41b5a9a..e082267 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java @@ -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; diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index a24464c..5ec5367 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -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 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 + "(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 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 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 exportToModules = exports.exportToModules; + if (exportToModules.size() > 0) { + buffer.append(" to").appendLineSeparator(); + appendFQClassNames(buffer, exportToModules); + } + buffer.append(';').appendLineSeparator(); + newLineNeeded = true; + } + } + } + + List 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 opensToModules = opens.opensToModules; + if (opensToModules.size() > 0) { + buffer.append(" to").appendLineSeparator(); + appendFQClassNames(buffer, opensToModules); + } + buffer.append(';').appendLineSeparator(); + newLineNeeded = true; + } + } + } + + List 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 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 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 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 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 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 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(); + } + } + } } \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index 1ee22f2..7bb50aa 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -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); } } diff --git a/src/org/jetbrains/java/decompiler/main/EnumProcessor.java b/src/org/jetbrains/java/decompiler/main/EnumProcessor.java index 08598e8..7f6beef 100644 --- a/src/org/jetbrains/java/decompiler/main/EnumProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/EnumProcessor.java @@ -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(); diff --git a/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java b/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java index 0e373a9..f2026c9 100644 --- a/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java @@ -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 diff --git a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java index 7b8c3b4..dc6b3a5 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java @@ -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 classes = DecompilerContext.getStructContext().getClasses(); LinkedList queue = new LinkedList<>(); + Set 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 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 packImports() { @@ -181,4 +183,4 @@ public class ImportCollector { .map(ent -> ent.getValue() + "." + ent.getKey()) .collect(Collectors.toList()); } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java index 57bae44..7837939 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java @@ -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(); diff --git a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java index 48f6aae..942d559 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java @@ -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; diff --git a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java index fd0f261..2ad5b20 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java @@ -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 bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1); MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor); @@ -123,4 +122,4 @@ public class LambdaProcessor { // FIXME: mixed hierarchy? } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index a992b20..de7a730 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -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); } @@ -200,4 +201,4 @@ public class MethodProcessorRunnable implements Runnable { public boolean isFinished() { return finished; } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java index 9d28d0e..13106f5 100644 --- a/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java @@ -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 setProtectedBlocks = new HashSet<>(); - setProtectedBlocks.addAll(range.getProtectedRange()); + Set 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) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java index 6e1155d..d6a7ed8 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java @@ -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 METHOD_CACHE = Collections.synchronizedMap(new HashMap<>()); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java index ede1dda..14576cc 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java @@ -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) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java index 7d5c207..3b255be 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java @@ -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"; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java index 1549734..50613fc 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java @@ -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 lst) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java index 23a5c7b..49d530f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java @@ -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) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java index 3b9847f..8e0ac9c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java @@ -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); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java index bc0269b..e7bc591 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java @@ -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 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 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 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 sample; private final BasicBlock next; @@ -981,4 +976,4 @@ public class FinallyProcessor { } } } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java index 9598b3c..ff45d29 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java @@ -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) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java index 89bfae3..66e857c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java @@ -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; } } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java index f6ee600..0c94761 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java @@ -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) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java index 9c150b5..bf4d889 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java @@ -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) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java index 8229127..b82562e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java @@ -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) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index fcdbdff..854f2b0 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -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); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java index a7c804b..beaa6c2 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java @@ -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 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; + } } } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java index 4fe4127..2e00b7a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java @@ -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) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java index 4e81646..fcea19f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java @@ -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); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java index b49bf39..1e08a6d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java @@ -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(); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java index d8b587f..ec1181d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java @@ -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 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) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java index 612ccdc..4a993b2 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java @@ -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 protectedRange; @@ -407,7 +407,7 @@ public class ExceptionDeobfuscator { for (ExceptionRangeCFG range : ranges) { - // add some dummy instructions to prevent optimizing away the empty block + // add some dummy instructions to prevent optimizing away the empty block SimpleInstructionSequence seq = new SimpleInstructionSequence(); seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), -1); seq.addInstruction(Instruction.create(CodeConstants.opc_pop, false, CodeConstants.GROUP_GENERAL, bytecode_version, null), -1); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java index 7fecbd9..9cab6a5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java @@ -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) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java index 302d62f..58a653a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java @@ -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 getSyntheticParametersMask(String className, String descriptor, int parameters) { ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(className); return node != null ? getSyntheticParametersMask(node, descriptor, parameters) : null; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java index e946c9a..30cbed4 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java @@ -26,7 +26,7 @@ import java.util.Set; public class NewExprent extends Exprent { private InvocationExprent constructor; private final VarType newType; - private List lstDims = new ArrayList<>(); + private final List lstDims; private List lstArrayElements = new ArrayList<>(); private boolean directArrayInit; private boolean isVarArgParam; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index 3eab706..9b0d01e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -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 = ""; @@ -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; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java index b9abd11..ca43d66 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java @@ -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; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java index 680d8b5..ffaebb9 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java @@ -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; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java index 58f6933..3a9ec9f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java @@ -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> exctstrings = new ArrayList<>(); private final List vars = new ArrayList<>(); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java index f9a149f..46b2f2c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java @@ -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; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java index bf18c51..c6a01d5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java @@ -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; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java index cadf50f..891ba35 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java @@ -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; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java index ab48ecc..a5b9d3a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java @@ -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 diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index 9999b50..8d766e0 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -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) { @@ -125,4 +121,4 @@ public class VarProcessor { public Set getExternalVars() { return externalVars; } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java index 3c19d2c..c31cfad 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java @@ -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); diff --git a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java index f9cd2af..c77cfaa 100644 --- a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java +++ b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java @@ -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); diff --git a/src/org/jetbrains/java/decompiler/struct/StructClass.java b/src/org/jetbrains/java/decompiler/struct/StructClass.java index 0bb370c..134813e 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructClass.java +++ b/src/org/jetbrains/java/decompiler/struct/StructClass.java @@ -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 fields; - private final VBStyleCollection 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); + VBStyleCollectionfields = 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); + VBStyleCollectionmethods = 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 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 fields; + private final VBStyleCollection methods; + + private ConstantPool pool; + + private StructClass(int accessFlags, + Map attributes, + String qualifiedName, + PrimitiveConstant superClass, + boolean own, + LazyLoader loader, + int minorVersion, + int majorVersion, + int[] interfaces, + String[] interfaceNames, + VBStyleCollection fields, + VBStyleCollection 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 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 diff --git a/src/org/jetbrains/java/decompiler/struct/StructContext.java b/src/org/jetbrains/java/decompiler/struct/StructContext.java index 6d07096..61486aa 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructContext.java +++ b/src/org/jetbrains/java/decompiler/struct/StructContext.java @@ -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)); @@ -160,4 +159,4 @@ public class StructContext { public Map getClasses() { return classes; } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/StructField.java b/src/org/jetbrains/java/decompiler/struct/StructField.java index 6c85ca4..cfa64d2 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructField.java +++ b/src/org/jetbrains/java/decompiler/struct/StructField.java @@ -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 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 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; } diff --git a/src/org/jetbrains/java/decompiler/struct/StructMember.java b/src/org/jetbrains/java/decompiler/struct/StructMember.java index a5b87f1..920e7f9 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMember.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMember.java @@ -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 attributes; +public abstract class StructMember { + private final int accessFlags; + private final Map attributes; + protected StructMember(int accessFlags, Map attributes) { + this.accessFlags = accessFlags; + this.attributes = attributes; + } public int getAccessFlags() { return accessFlags; } public T getAttribute(StructGeneralAttribute.Key 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 readAttributes(DataInputFullStream in, ConstantPool pool) throws IOException { + public static Map readAttributes(DataInputFullStream in, ConstantPool pool) throws IOException { int length = in.readUnsignedShort(); - Map 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; - } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/StructMethod.java b/src/org/jetbrains/java/decompiler/struct/StructMethod.java index 859805c..a715593 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMethod.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMethod.java @@ -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 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 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 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 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() { @@ -387,4 +371,4 @@ public class StructMethod extends StructMember { public String toString() { return name; } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java b/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java new file mode 100644 index 0000000..e50983d --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java @@ -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 attributes = readAttributes(in, pool); + + return new StructRecordComponent(0, attributes, name, descriptor); + } + + private StructRecordComponent(int flags, Map attributes, String name, String descriptor) { + super(flags, attributes, name, descriptor); + } +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java new file mode 100644 index 0000000..87f02be --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java @@ -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 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); + } +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java index 02ddbec..4099386 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java @@ -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 ATTRIBUTE_CODE = new Key<>("Code"); + public static final Key ATTRIBUTE_CODE = new Key<>("Code"); public static final Key ATTRIBUTE_INNER_CLASSES = new Key<>("InnerClasses"); public static final Key ATTRIBUTE_SIGNATURE = new Key<>("Signature"); public static final Key ATTRIBUTE_ANNOTATION_DEFAULT = new Key<>("AnnotationDefault"); @@ -34,85 +34,81 @@ public class StructGeneralAttribute { public static final Key ATTRIBUTE_DEPRECATED = new Key<>("Deprecated"); public static final Key ATTRIBUTE_LINE_NUMBER_TABLE = new Key<>("LineNumberTable"); public static final Key ATTRIBUTE_METHOD_PARAMETERS = new Key<>("MethodParameters"); + public static final Key ATTRIBUTE_MODULE = new Key<>("Module"); + public static final Key ATTRIBUTE_RECORD = new Key<>("Record"); public static final Key ATTRIBUTE_ORIGINAL_PC_TABLE = new Key<>("OriginalPcTable"); + @SuppressWarnings("unused") public static class Key { - 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; - } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java index 4306381..238900d 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java @@ -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; diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java index 15fb317..66eb593 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java @@ -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; diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java new file mode 100644 index 0000000..9fd6f89 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java @@ -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 requires; + public List exports; + public List opens; + public List uses; + public List 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 readRequires(DataInputFullStream data, ConstantPool pool) throws IOException { + int requiresCount = data.readUnsignedShort(); + if (requiresCount <= 0) return Collections.emptyList(); + + List 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 readExports(DataInputFullStream data, ConstantPool pool) throws IOException { + int exportsCount = data.readUnsignedShort(); + if (exportsCount <= 0) return Collections.emptyList(); + + List exports = new ArrayList<>(exportsCount); + for (int i = 0; i < exportsCount; i++) { + int packageNameIndex = data.readUnsignedShort(); + int exportsFlags = data.readUnsignedShort(); + List exportsToModules = readStringList(data, pool); + String packageName = pool.getPrimitiveConstant(packageNameIndex).getString(); + exports.add(new ExportsEntry(packageName, exportsFlags, exportsToModules)); + } + return exports; + } + + private static List readOpens(DataInputFullStream data, ConstantPool pool) throws IOException { + int opensCount = data.readUnsignedShort(); + if (opensCount <= 0) return Collections.emptyList(); + + List opens = new ArrayList<>(opensCount); + for (int i = 0; i < opensCount; i++) { + int packageNameIndex = data.readUnsignedShort(); + int opensFlags = data.readUnsignedShort(); + List opensToModules = readStringList(data, pool); + String packageName = pool.getPrimitiveConstant(packageNameIndex).getString(); + opens.add(new OpensEntry(packageName, opensFlags, opensToModules)); + } + return opens; + } + + private static List readUses(DataInputFullStream data, ConstantPool pool) throws IOException { + return readStringList(data, pool); + } + + private static List readProvides(DataInputFullStream data, ConstantPool pool) throws IOException { + int providesCount = data.readUnsignedShort(); + if (providesCount <= 0) return Collections.emptyList(); + + List provides = new ArrayList<>(providesCount); + for (int i = 0; i < providesCount; i++) { + int interfaceNameIndex = data.readUnsignedShort(); + String interfaceName = pool.getPrimitiveConstant(interfaceNameIndex).getString(); + List implementationNames = readStringList(data, pool); + provides.add(new ProvidesEntry(interfaceName, implementationNames)); + } + return provides; + } + + private static List readStringList(DataInputFullStream data, ConstantPool pool) throws IOException { + int count = data.readUnsignedShort(); + if (count <= 0) { + return Collections.emptyList(); + } + else { + List 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 exportToModules; + + public ExportsEntry(String packageName, int flags, List 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 opensToModules; + + public OpensEntry(String packageName, int flags, List exportToModules) { + this.packageName = packageName; + this.flags = flags; + this.opensToModules = exportToModules; + } + } + + public static final class ProvidesEntry { + public final String interfaceName; + public final List implementationNames; + + public ProvidesEntry(String interfaceName, List implementationNames) { + this.interfaceName = interfaceName; + this.implementationNames = implementationNames; + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java new file mode 100644 index 0000000..24c6152 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java @@ -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 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 getComponents() { + return Collections.unmodifiableList(components); + } +} diff --git a/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java index 0ea6e01..3280038 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java @@ -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(); } @@ -236,4 +232,4 @@ public class ConstantPool implements NewClassNameBuilder { return MethodDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this); } } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java b/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java index d8a92ca..b38800d 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java @@ -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); } } } diff --git a/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java b/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java index 626a91b..408622a 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java @@ -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(); } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java index 94add14..50d41ba 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java @@ -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;"); diff --git a/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java index 102393c..2ad7c68 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java @@ -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) { diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java index 3e4e1f1..8af6b50 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java @@ -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", diff --git a/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java index 63c0f8e..8b6105d 100644 --- a/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java +++ b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java @@ -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; } @@ -138,4 +139,4 @@ public class LazyLoader { this.internalPath = internalPath; } } -} \ No newline at end of file +} diff --git a/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java b/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java index d05b7d4..265a538 100644 --- a/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java +++ b/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java @@ -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 { @@ -46,7 +43,7 @@ public class FastFixedSetFactory { return colValuesInternal; } - public static class FastFixedSet implements Iterable { + public static final class FastFixedSet implements Iterable { private final FastFixedSetFactory factory; @@ -66,8 +63,7 @@ public class FastFixedSetFactory { FastFixedSet 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 { } } - public static class FastFixedSetIterator implements Iterator { + public static final class FastFixedSetIterator implements Iterator { private final VBStyleCollection colValuesInternal; private final int[] data; diff --git a/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java b/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java index b955139..9b66a2e 100644 --- a/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java +++ b/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java @@ -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 { @@ -69,7 +66,7 @@ public class FastSparseSetFactory { } - public static class FastSparseSet implements Iterable { + public static final class FastSparseSet implements Iterable { public static final FastSparseSet[] EMPTY_ARRAY = new FastSparseSet[0]; private final FastSparseSetFactory factory; @@ -97,15 +94,7 @@ public class FastSparseSetFactory { } public FastSparseSet 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 { 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 { } } - public static class FastSparseSetIterator implements Iterator { + public static final class FastSparseSetIterator implements Iterator { private final VBStyleCollection colValuesInternal; private final int[] data; diff --git a/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java b/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java index 1d29253..0ba7dc4 100644 --- a/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java +++ b/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java @@ -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]; diff --git a/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java b/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java index 16a0484..bb8b4dd 100644 --- a/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java +++ b/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java @@ -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[] 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>() { + list.add(new Entry<>() { private final Integer var = key; private final FastSparseSet val = ent; diff --git a/src/org/jetbrains/java/decompiler/util/TextUtil.java b/src/org/jetbrains/java/decompiler/util/TextUtil.java index 3d92d2e..e2a08f0 100644 --- a/src/org/jetbrains/java/decompiler/util/TextUtil.java +++ b/src/org/jetbrains/java/decompiler/util/TextUtil.java @@ -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 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", diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index 1d0dc62..e0a9b6f 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -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(); @@ -169,4 +186,4 @@ public class SingleClassesTest { return files; } -} \ No newline at end of file +} diff --git a/testData/classes/java9/module-info.class b/testData/classes/java9/module-info.class new file mode 100644 index 0000000..cc01403 Binary files /dev/null and b/testData/classes/java9/module-info.class differ diff --git a/testData/classes/pkg/TestInheritanceChainCycle.class b/testData/classes/pkg/TestInheritanceChainCycle.class new file mode 100644 index 0000000..0969e39 Binary files /dev/null and b/testData/classes/pkg/TestInheritanceChainCycle.class differ diff --git a/testData/classes/pkg/TestInvertedFloatComparison.class b/testData/classes/pkg/TestInvertedFloatComparison.class new file mode 100644 index 0000000..29c1432 Binary files /dev/null and b/testData/classes/pkg/TestInvertedFloatComparison.class differ diff --git a/testData/classes/pkg/package-info.class b/testData/classes/pkg/package-info.class index eee2939..88f6acc 100644 Binary files a/testData/classes/pkg/package-info.class and b/testData/classes/pkg/package-info.class differ diff --git a/testData/classes/records/TestRecordAnno.class b/testData/classes/records/TestRecordAnno.class new file mode 100644 index 0000000..6751bc0 Binary files /dev/null and b/testData/classes/records/TestRecordAnno.class differ diff --git a/testData/classes/records/TestRecordEmpty.class b/testData/classes/records/TestRecordEmpty.class new file mode 100644 index 0000000..027b700 Binary files /dev/null and b/testData/classes/records/TestRecordEmpty.class differ diff --git a/testData/classes/records/TestRecordGenericVararg.class b/testData/classes/records/TestRecordGenericVararg.class new file mode 100644 index 0000000..52bb9c6 Binary files /dev/null and b/testData/classes/records/TestRecordGenericVararg.class differ diff --git a/testData/classes/records/TestRecordSimple.class b/testData/classes/records/TestRecordSimple.class new file mode 100644 index 0000000..91c748e Binary files /dev/null and b/testData/classes/records/TestRecordSimple.class differ diff --git a/testData/classes/records/TestRecordVararg.class b/testData/classes/records/TestRecordVararg.class new file mode 100644 index 0000000..a720d7f Binary files /dev/null and b/testData/classes/records/TestRecordVararg.class differ diff --git a/testData/results/TestInheritanceChainCycle.dec b/testData/results/TestInheritanceChainCycle.dec new file mode 100644 index 0000000..f2490af --- /dev/null +++ b/testData/results/TestInheritanceChainCycle.dec @@ -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 diff --git a/testData/results/TestInvertedFloatComparison.dec b/testData/results/TestInvertedFloatComparison.dec new file mode 100644 index 0000000..e252925 --- /dev/null +++ b/testData/results/TestInvertedFloatComparison.dec @@ -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 diff --git a/testData/results/TestRecordAnno.dec b/testData/results/TestRecordAnno.dec new file mode 100644 index 0000000..931c3e6 --- /dev/null +++ b/testData/results/TestRecordAnno.dec @@ -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 ' (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 diff --git a/testData/results/TestRecordEmpty.dec b/testData/results/TestRecordEmpty.dec new file mode 100644 index 0000000..9ac6460 --- /dev/null +++ b/testData/results/TestRecordEmpty.dec @@ -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 diff --git a/testData/results/TestRecordGenericVararg.dec b/testData/results/TestRecordGenericVararg.dec new file mode 100644 index 0000000..1d417b4 --- /dev/null +++ b/testData/results/TestRecordGenericVararg.dec @@ -0,0 +1,39 @@ +package records; + +public record TestRecordGenericVararg(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 ' (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 diff --git a/testData/results/TestRecordSimple.dec b/testData/results/TestRecordSimple.dec new file mode 100644 index 0000000..222805f --- /dev/null +++ b/testData/results/TestRecordSimple.dec @@ -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 ' (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 diff --git a/testData/results/TestRecordVararg.dec b/testData/results/TestRecordVararg.dec new file mode 100644 index 0000000..850ed38 --- /dev/null +++ b/testData/results/TestRecordVararg.dec @@ -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 ' (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 diff --git a/testData/results/module-info.dec b/testData/results/module-info.dec new file mode 100644 index 0000000..a2018a5 --- /dev/null +++ b/testData/results/module-info.dec @@ -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; +} diff --git a/testData/results/package-info.dec b/testData/results/package-info.dec index f19fbea..0ccd581 100644 --- a/testData/results/package-info.dec +++ b/testData/results/package-info.dec @@ -1,2 +1,4 @@ -@jdk.Exported -package pkg; \ No newline at end of file +@PkgAnno("...") +package pkg; + +import ext.PkgAnno; diff --git a/testData/src/ext/PkgAnno.java b/testData/src/ext/PkgAnno.java new file mode 100644 index 0000000..683bd14 --- /dev/null +++ b/testData/src/ext/PkgAnno.java @@ -0,0 +1,8 @@ +package ext; + +import java.lang.annotation.*; + +@Target(ElementType.PACKAGE) +public @interface PkgAnno { + String value(); +} diff --git a/testData/src/java9/sample.module/TestClass.java b/testData/src/java9/sample.module/TestClass.java new file mode 100644 index 0000000..0a858dd --- /dev/null +++ b/testData/src/java9/sample.module/TestClass.java @@ -0,0 +1,3 @@ +package sample.pkg2; + +public class TestClass {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/TestModuleAnno.java b/testData/src/java9/sample.module/TestModuleAnno.java new file mode 100644 index 0000000..192ee2b --- /dev/null +++ b/testData/src/java9/sample.module/TestModuleAnno.java @@ -0,0 +1,8 @@ +package sample.pkg1; + +import java.lang.annotation.*; + +@Target(ElementType.MODULE) +public @interface TestModuleAnno { + String value(); +} diff --git a/testData/src/java9/sample.module/TestService.java b/testData/src/java9/sample.module/TestService.java new file mode 100644 index 0000000..d73510f --- /dev/null +++ b/testData/src/java9/sample.module/TestService.java @@ -0,0 +1,3 @@ +package sample.pkg1; + +public interface TestService {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/TestServiceImpl.java b/testData/src/java9/sample.module/TestServiceImpl.java new file mode 100644 index 0000000..0463d99 --- /dev/null +++ b/testData/src/java9/sample.module/TestServiceImpl.java @@ -0,0 +1,3 @@ +package sample.pkg1; + +public class TestServiceImpl implements TestService {} \ No newline at end of file diff --git a/testData/src/java9/sample.module/module-info.java b/testData/src/java9/sample.module/module-info.java new file mode 100644 index 0000000..9f42e18 --- /dev/null +++ b/testData/src/java9/sample.module/module-info.java @@ -0,0 +1,18 @@ +import sample.pkg1.TestModuleAnno; + +@TestModuleAnno("...") +module sample.module { + requires java.desktop; + + uses java.util.spi.ToolProvider; + + provides sample.pkg1.TestService with sample.pkg1.TestServiceImpl; + + exports sample.pkg1; + + exports sample.pkg2 to java.base; + + opens sample.pkg1; + + opens sample.pkg2 to java.base; +} diff --git a/testData/src/pkg/TestInheritanceChainCycle.jasm b/testData/src/pkg/TestInheritanceChainCycle.jasm new file mode 100644 index 0000000..b6ccdb6 --- /dev/null +++ b/testData/src/pkg/TestInheritanceChainCycle.jasm @@ -0,0 +1,27 @@ +/** + * This code can be assembled with asmtools + * using asmtools jasm -g *.jasm command line. + */ +package pkg; + +super public class TestInheritanceChainCycle + extends TestInheritanceChainCycle + version 52:0 +{ + public Method "":"()V" + stack 1 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } + public Method printMessage:"()V" + stack 2 locals 1 + { + getstatic Field java/lang/System.out:"Ljava/io/PrintStream;"; + ldc String "Hello, bug!"; + invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V"; + return; + } + +} // end Class TestInheritanceChainCycle diff --git a/testData/src/pkg/TestInvertedFloatComparison.java b/testData/src/pkg/TestInvertedFloatComparison.java new file mode 100644 index 0000000..d7a06b4 --- /dev/null +++ b/testData/src/pkg/TestInvertedFloatComparison.java @@ -0,0 +1,83 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package pkg; + +public class TestInvertedFloatComparison { + + public boolean less(double a, double b) { + return a < b; + } + + public boolean less(int a, int b) { + return a < b; + } + + public boolean notLess(double a, double b) { + return !(a < b); + } + + public boolean notLess(int a, int b) { + return !(a < b); + } + + public boolean greater(double a, double b) { + return a > b; + } + + public boolean greater(int a, int b) { + return a > b; + } + + public boolean notGreater(double a, double b) { + return !(a > b); + } + + public boolean notGreater(int a, int b) { + return !(a > b); + } + + public boolean lessEqual(double a, double b) { + return a <= b; + } + + public boolean lessEqual(int a, int b) { + return a <= b; + } + + public boolean notLessEqual(double a, double b) { + return !(a <= b); + } + + public boolean notLessEqual(int a, int b) { + return !(a <= b); + } + + public boolean greaterEqual(double a, double b) { + return a >= b; + } + + public boolean greaterEqual(int a, int b) { + return a >= b; + } + + public boolean notGreaterEqual(double a, double b) { + return !(a >= b); + } + + public boolean notGreaterEqual(int a, int b) { + return !(a >= b); + } +} \ No newline at end of file diff --git a/testData/src/pkg/package-info.java b/testData/src/pkg/package-info.java index d665652..0ccd581 100644 --- a/testData/src/pkg/package-info.java +++ b/testData/src/pkg/package-info.java @@ -1,2 +1,4 @@ -@jdk.Exported +@PkgAnno("...") package pkg; + +import ext.PkgAnno; diff --git a/testData/src/records/TestRecordAnno.java b/testData/src/records/TestRecordAnno.java new file mode 100644 index 0000000..c61a5b5 --- /dev/null +++ b/testData/src/records/TestRecordAnno.java @@ -0,0 +1,17 @@ +package records; + +import java.lang.annotation.*; + +public record TestRecordAnno(@TA @RC int x, @M @P int y) {} + +@Target(ElementType.TYPE_USE) +@interface TA {} + +@Target(ElementType.RECORD_COMPONENT) +@interface RC {} + +@Target(ElementType.METHOD) +@interface M {} + +@Target(ElementType.PARAMETER) +@interface P {} diff --git a/testData/src/records/TestRecordEmpty.java b/testData/src/records/TestRecordEmpty.java new file mode 100644 index 0000000..f478efa --- /dev/null +++ b/testData/src/records/TestRecordEmpty.java @@ -0,0 +1,7 @@ +package records; + +public record TestRecordEmpty() { + public int hashCode() { + return 0; + } +} \ No newline at end of file diff --git a/testData/src/records/TestRecordGenericVararg.java b/testData/src/records/TestRecordGenericVararg.java new file mode 100644 index 0000000..44e8fdc --- /dev/null +++ b/testData/src/records/TestRecordGenericVararg.java @@ -0,0 +1,6 @@ +package records; + +public record TestRecordGenericVararg(T first, T... other) { + @SafeVarargs + public TestRecordGenericVararg {} +} \ No newline at end of file diff --git a/testData/src/records/TestRecordSimple.java b/testData/src/records/TestRecordSimple.java new file mode 100644 index 0000000..3bc1e1a --- /dev/null +++ b/testData/src/records/TestRecordSimple.java @@ -0,0 +1,3 @@ +package records; + +public record TestRecordSimple(int x, int y) {} \ No newline at end of file diff --git a/testData/src/records/TestRecordVararg.java b/testData/src/records/TestRecordVararg.java new file mode 100644 index 0000000..8d23ed0 --- /dev/null +++ b/testData/src/records/TestRecordVararg.java @@ -0,0 +1,3 @@ +package records; + +public record TestRecordVararg(int x, int[]... y) {} \ No newline at end of file