From d4d5a1041b12f772dc542e01ef19ab6b183752c0 Mon Sep 17 00:00:00 2001 From: Reece Date: Sun, 26 Sep 2021 00:06:46 +0100 Subject: [PATCH] [*] Added a few gross patches to the intellij source tree [*] Lazily moved intellijs source tree over --- pom.xml | 23 +- .../java/decompiler/code/CodeConstants.java | 69 +- .../java/decompiler/code/ConstantsUtil.java | 482 -------- .../decompiler/code/ExceptionHandler.java | 42 +- .../java/decompiler/code/ExceptionTable.java | 49 +- .../code/FullInstructionSequence.java | 18 +- .../java/decompiler/code/IfInstruction.java | 36 - .../java/decompiler/code/Instruction.java | 153 +-- .../decompiler/code/InstructionSequence.java | 127 +- .../java/decompiler/code/JumpInstruction.java | 40 +- .../code/SimpleInstructionSequence.java | 23 +- .../decompiler/code/SwitchInstruction.java | 82 +- .../java/decompiler/code/cfg/BasicBlock.java | 121 +- .../decompiler/code/cfg/ControlFlowGraph.java | 176 +-- .../code/cfg/ExceptionRangeCFG.java | 77 +- .../code/interpreter/InstructionImpact.java | 69 +- .../decompiler/code/interpreter/Util.java | 286 ----- .../code/optinstructions/ALOAD.java | 60 - .../code/optinstructions/ANEWARRAY.java | 33 - .../code/optinstructions/ASTORE.java | 60 - .../code/optinstructions/BIPUSH.java | 48 - .../code/optinstructions/CHECKCAST.java | 34 - .../code/optinstructions/DLOAD.java | 60 - .../code/optinstructions/DSTORE.java | 60 - .../code/optinstructions/FLOAD.java | 60 - .../code/optinstructions/FSTORE.java | 60 - .../code/optinstructions/GETFIELD.java | 33 - .../code/optinstructions/GETSTATIC.java | 33 - .../decompiler/code/optinstructions/GOTO.java | 46 - .../code/optinstructions/GOTO_W.java | 33 - .../decompiler/code/optinstructions/IINC.java | 43 - .../code/optinstructions/ILOAD.java | 55 - .../code/optinstructions/INSTANCEOF.java | 33 - .../code/optinstructions/INVOKEDYNAMIC.java | 35 - .../code/optinstructions/INVOKEINTERFACE.java | 35 - .../code/optinstructions/INVOKESPECIAL.java | 33 - .../code/optinstructions/INVOKESTATIC.java | 33 - .../code/optinstructions/INVOKEVIRTUAL.java | 33 - .../code/optinstructions/ISTORE.java | 55 - .../decompiler/code/optinstructions/JSR.java | 46 - .../code/optinstructions/JSR_W.java | 33 - .../decompiler/code/optinstructions/LDC.java | 33 - .../code/optinstructions/LDC2_W.java | 33 - .../code/optinstructions/LDC_W.java | 33 - .../code/optinstructions/LLOAD.java | 55 - .../code/optinstructions/LOOKUPSWITCH.java | 42 - .../code/optinstructions/LSTORE.java | 55 - .../code/optinstructions/MULTIANEWARRAY.java | 34 - .../decompiler/code/optinstructions/NEW.java | 33 - .../code/optinstructions/NEWARRAY.java | 33 - .../code/optinstructions/PUTFIELD.java | 33 - .../code/optinstructions/PUTSTATIC.java | 33 - .../decompiler/code/optinstructions/RET.java | 41 - .../code/optinstructions/SIPUSH.java | 33 - .../code/optinstructions/TABLESWITCH.java | 42 - .../java/decompiler/main/AssertProcessor.java | 125 +- .../main/ClassReference14Processor.java | 160 +-- .../java/decompiler/main/ClassWriter.java | 947 +++++++++------ .../decompiler/main/ClassesProcessor.java | 463 ++++---- .../decompiler/main/DecompilerContext.java | 189 ++- .../java/decompiler/main/EnumProcessor.java | 78 +- .../java/decompiler/main/Fernflower.java | 125 +- .../decompiler/main/InitializerProcessor.java | 208 ++-- .../java/decompiler/main/TextBuffer.java | 187 --- .../collectors/BytecodeMappingTracer.java | 95 +- .../main/collectors/BytecodeSourceMapper.java | 108 +- .../main/collectors/CounterContainer.java | 23 +- .../main/collectors/ImportCollector.java | 229 ++-- .../main/collectors/VarNamesCollector.java | 169 ++- .../main/decompiler/BaseDecompiler.java | 37 +- .../main/decompiler/ConsoleDecompiler.java | 129 +- .../main/decompiler/PrintStreamLogger.java | 75 +- .../main/extern/IBytecodeProvider.java | 16 +- .../main/extern/IFernflowerLogger.java | 22 +- .../main/extern/IFernflowerPreferences.java | 101 +- .../main/extern/IIdentifierRenamer.java | 32 +- .../decompiler/main/extern/IResultSaver.java | 18 +- .../decompiler/main/rels/ClassWrapper.java | 177 +-- .../decompiler/main/rels/LambdaProcessor.java | 60 +- ...read.java => MethodProcessorRunnable.java} | 179 +-- .../decompiler/main/rels/MethodWrapper.java | 49 +- .../main/rels/NestedClassProcessor.java | 1035 +++++++---------- .../main/rels/NestedMemberAccess.java | 145 ++- .../modules/code/DeadCodeHelper.java | 211 +++- .../modules/decompiler/ClasspathHelper.java | 72 ++ .../modules/decompiler/ClearStructHelper.java | 20 +- .../decompiler/ConcatenationHelper.java | 97 +- .../modules/decompiler/DecHelper.java | 55 +- .../modules/decompiler/DomHelper.java | 99 +- .../decompiler/EliminateLoopsHelper.java | 214 ---- .../modules/decompiler/ExitHelper.java | 180 +-- .../modules/decompiler/ExprProcessor.java | 603 +++++----- .../modules/decompiler/ExprentStack.java | 24 +- .../modules/decompiler/FinallyProcessor.java | 298 ++--- .../modules/decompiler/IdeaNotNullHelper.java | 63 +- .../modules/decompiler/IfHelper.java | 164 +-- .../decompiler/InlineSingleBlockHelper.java | 41 +- .../modules/decompiler/LabelHelper.java | 64 +- .../modules/decompiler/LoopExtractHelper.java | 29 +- .../modules/decompiler/LowBreakHelper.java | 208 ---- .../modules/decompiler/MergeHelper.java | 78 +- .../modules/decompiler/PPandMMHelper.java | 35 +- .../decompiler/PrimitiveExprsList.java | 18 +- .../decompiler/SecondaryFunctionsHelper.java | 152 +-- .../modules/decompiler/SequenceHelper.java | 28 +- .../decompiler/SimplifyExprentsHelper.java | 605 +++++----- .../decompiler/StackVarsProcessor.java | 197 +--- .../modules/decompiler/StatEdge.java | 21 +- .../decompiler/StrongConnectivityHelper.java | 168 +-- .../modules/decompiler/SwitchHelper.java | 80 ++ .../decompiler/decompose/DominatorEngine.java | 20 +- .../DominatorTreeExceptionFilter.java | 63 +- .../FastExtendedPostdominanceHelper.java | 203 ++-- .../decompose/GenericDominatorEngine.java | 24 +- .../modules/decompiler/decompose/IGraph.java | 16 +- .../decompiler/decompose/IGraphNode.java | 16 +- .../deobfuscator/ExceptionDeobfuscator.java | 210 +++- .../IrreducibleCFGDeobfuscator.java | 36 +- .../decompiler/exps/AnnotationExprent.java | 126 +- .../modules/decompiler/exps/ArrayExprent.java | 98 +- .../decompiler/exps/AssertExprent.java | 35 +- .../decompiler/exps/AssignmentExprent.java | 150 ++- .../modules/decompiler/exps/ConstExprent.java | 518 +++++---- .../modules/decompiler/exps/ExitExprent.java | 142 +-- .../modules/decompiler/exps/ExprUtil.java | 45 + .../modules/decompiler/exps/Exprent.java | 138 ++- .../modules/decompiler/exps/FieldExprent.java | 159 +-- .../decompiler/exps/FunctionExprent.java | 425 ++++--- .../modules/decompiler/exps/IfExprent.java | 84 +- .../decompiler/exps/InvocationExprent.java | 478 +++++--- .../decompiler/exps/MonitorExprent.java | 68 +- .../modules/decompiler/exps/NewExprent.java | 539 +++++---- .../decompiler/exps/SwitchExprent.java | 83 +- .../decompiler/exps/TypeAnnotation.java | 53 + .../modules/decompiler/exps/VarExprent.java | 227 ++-- .../decompiler/sforms/DirectGraph.java | 40 +- .../modules/decompiler/sforms/DirectNode.java | 28 +- .../sforms/FlattenStatementsHelper.java | 116 +- .../sforms/SSAConstructorSparseEx.java | 69 +- .../sforms/SSAUConstructorSparseEx.java | 150 +-- .../decompiler/stats/BasicBlockStatement.java | 29 +- .../decompiler/stats/CatchAllStatement.java | 91 +- .../decompiler/stats/CatchStatement.java | 91 +- .../modules/decompiler/stats/DoStatement.java | 82 +- .../decompiler/stats/DummyExitStatement.java | 25 + .../decompiler/stats/GeneralStatement.java | 43 +- .../modules/decompiler/stats/IfStatement.java | 171 ++- .../decompiler/stats/RootStatement.java | 38 +- .../decompiler/stats/SequenceStatement.java | 46 +- .../modules/decompiler/stats/Statement.java | 188 +-- .../modules/decompiler/stats/Statements.java | 44 + .../decompiler/stats/SwitchStatement.java | 189 +-- .../stats/SynchronizedStatement.java | 94 +- .../decompiler/vars/CheckTypesResult.java | 36 +- .../decompiler/vars/VarDefinitionHelper.java | 86 +- .../modules/decompiler/vars/VarProcessor.java | 140 +-- .../decompiler/vars/VarTypeProcessor.java | 235 ++-- .../decompiler/vars/VarVersionEdge.java | 28 +- .../decompiler/vars/VarVersionNode.java | 33 +- .../decompiler/vars/VarVersionPaar.java | 77 -- .../decompiler/vars/VarVersionPair.java | 49 + .../decompiler/vars/VarVersionsGraph.java | 73 +- .../decompiler/vars/VarVersionsProcessor.java | 339 +++--- .../modules/renamer/ClassWrapperNode.java | 35 +- .../modules/renamer/ConverterHelper.java | 173 ++- .../modules/renamer/IdentifierConverter.java | 266 ++--- .../modules/renamer/PoolInterceptor.java | 37 +- .../java/decompiler/struct/ContextUnit.java | 47 +- .../decompiler/struct/IDecompiledData.java | 16 +- .../java/decompiler/struct/StructClass.java | 168 +-- .../java/decompiler/struct/StructContext.java | 49 +- .../java/decompiler/struct/StructField.java | 54 +- .../java/decompiler/struct/StructMember.java | 85 +- .../java/decompiler/struct/StructMethod.java | 216 ++-- .../struct/StructRecordComponent.java | 36 + .../attr/StructAnnDefaultAttribute.java | 21 +- .../attr/StructAnnotationAttribute.java | 56 +- .../StructAnnotationParameterAttribute.java | 24 +- .../attr/StructAnnotationTypeAttribute.java | 194 --- .../attr/StructBootstrapMethodsAttribute.java | 28 +- .../struct/attr/StructCodeAttribute.java | 43 + .../attr/StructConstantValueAttribute.java | 21 +- .../attr/StructEnclosingMethodAttribute.java | 21 +- .../attr/StructExceptionsAttribute.java | 25 +- .../struct/attr/StructGeneralAttribute.java | 158 ++- .../attr/StructGenericSignatureAttribute.java | 21 +- .../attr/StructInnerClassesAttribute.java | 82 +- .../attr/StructLineNumberTableAttribute.java | 38 +- .../StructLocalVariableTableAttribute.java | 86 +- ...StructLocalVariableTypeTableAttribute.java | 34 + .../attr/StructMethodParametersAttribute.java | 56 + .../struct/attr/StructModuleAttribute.java | 165 +++ .../struct/attr/StructRecordAttribute.java | 37 + .../attr/StructTypeAnnotationAttribute.java | 92 ++ .../struct/consts/ConstantPool.java | 160 +-- .../struct/consts/LinkConstant.java | 128 +- .../struct/consts/PooledConstant.java | 115 +- .../struct/consts/PrimitiveConstant.java | 102 +- .../struct/consts/VariableTypeEnum.java | 47 - .../java/decompiler/struct/gen/DataPoint.java | 27 +- .../struct/gen/FieldDescriptor.java | 52 +- .../struct/gen/MethodDescriptor.java | 174 +-- .../struct/gen/NewClassNameBuilder.java | 6 + .../java/decompiler/struct/gen/VarType.java | 644 +++++----- .../gen/generics/GenericClassDescriptor.java | 22 +- .../gen/generics/GenericFieldDescriptor.java | 23 +- .../struct/gen/generics/GenericMain.java | 159 ++- .../gen/generics/GenericMethodDescriptor.java | 47 +- .../struct/gen/generics/GenericType.java | 165 +-- .../decompiler/struct/lazy/LazyLoader.java | 81 +- .../decompiler/struct/match/IMatchable.java | 30 + .../decompiler/struct/match/MatchEngine.java | 226 ++++ .../decompiler/struct/match/MatchNode.java | 66 ++ .../decompiler/util/DataInputFullStream.java | 45 +- .../decompiler/util/FastFixedSetFactory.java | 86 +- .../java/decompiler/util/FastSetFactory.java | 489 -------- .../decompiler/util/FastSparseSetFactory.java | 179 +-- .../java/decompiler/util/InterpreterUtil.java | 129 +- .../java/decompiler/util/ListStack.java | 42 +- .../decompiler/util/SFormsFastMapDirect.java | 46 +- .../java/decompiler/util/SortUtil.java | 65 -- .../java/decompiler/util/TextBuffer.java | 285 +++++ .../java/decompiler/util/TextUtil.java | 282 +++++ .../jetbrains/java/decompiler/util/Util.java | 12 - .../decompiler/util/VBStyleCollection.java | 82 +- .../java/decompiler/util/VarHelper.java | 104 -- .../spigotmc/fernflower/EclipseFormatter.java | 40 - 227 files changed, 10320 insertions(+), 15275 deletions(-) delete mode 100644 src/org/jetbrains/java/decompiler/code/ConstantsUtil.java delete mode 100644 src/org/jetbrains/java/decompiler/code/IfInstruction.java delete mode 100644 src/org/jetbrains/java/decompiler/code/interpreter/Util.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/ALOAD.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/ANEWARRAY.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/ASTORE.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/BIPUSH.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/CHECKCAST.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/DLOAD.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/DSTORE.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/FLOAD.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/FSTORE.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/GETFIELD.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/GETSTATIC.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/GOTO.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/GOTO_W.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/IINC.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/ILOAD.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/INSTANCEOF.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEDYNAMIC.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEINTERFACE.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESPECIAL.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESTATIC.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEVIRTUAL.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/ISTORE.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/JSR.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/JSR_W.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/LDC.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/LDC2_W.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/LDC_W.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/LLOAD.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/LOOKUPSWITCH.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/LSTORE.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/MULTIANEWARRAY.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/NEW.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/NEWARRAY.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/PUTFIELD.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/PUTSTATIC.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/RET.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/SIPUSH.java delete mode 100644 src/org/jetbrains/java/decompiler/code/optinstructions/TABLESWITCH.java delete mode 100644 src/org/jetbrains/java/decompiler/main/TextBuffer.java rename src/org/jetbrains/java/decompiler/main/rels/{MethodProcessorThread.java => MethodProcessorRunnable.java} (51%) create mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java delete mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java delete mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java create mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java create mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java create mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/exps/TypeAnnotation.java create mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/stats/DummyExitStatement.java create mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java delete mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java create mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPair.java create mode 100644 src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java delete mode 100644 src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java create mode 100644 src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java create mode 100644 src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTypeTableAttribute.java create mode 100644 src/org/jetbrains/java/decompiler/struct/attr/StructMethodParametersAttribute.java create mode 100644 src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java create mode 100644 src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java create mode 100644 src/org/jetbrains/java/decompiler/struct/attr/StructTypeAnnotationAttribute.java delete mode 100644 src/org/jetbrains/java/decompiler/struct/consts/VariableTypeEnum.java create mode 100644 src/org/jetbrains/java/decompiler/struct/gen/NewClassNameBuilder.java create mode 100644 src/org/jetbrains/java/decompiler/struct/match/IMatchable.java create mode 100644 src/org/jetbrains/java/decompiler/struct/match/MatchEngine.java create mode 100644 src/org/jetbrains/java/decompiler/struct/match/MatchNode.java delete mode 100644 src/org/jetbrains/java/decompiler/util/FastSetFactory.java delete mode 100644 src/org/jetbrains/java/decompiler/util/SortUtil.java create mode 100644 src/org/jetbrains/java/decompiler/util/TextBuffer.java create mode 100644 src/org/jetbrains/java/decompiler/util/TextUtil.java delete mode 100644 src/org/jetbrains/java/decompiler/util/Util.java delete mode 100644 src/org/jetbrains/java/decompiler/util/VarHelper.java delete mode 100644 src/org/spigotmc/fernflower/EclipseFormatter.java diff --git a/pom.xml b/pom.xml index 340282c..8941020 100644 --- a/pom.xml +++ b/pom.xml @@ -13,37 +13,20 @@ - - org.spigotmc - eclipse-formatter - 3.14.0-spigotmc-SNAPSHOT - + ./src - - net.md-5 - determiner - 0.1 - - - process-classes - - transform - - - - org.apache.maven.plugins maven-compiler-plugin 3.2 - 1.6 - 1.6 + 9 + 9 diff --git a/src/org/jetbrains/java/decompiler/code/CodeConstants.java b/src/org/jetbrains/java/decompiler/code/CodeConstants.java index 4a7ef0f..2c5cb42 100644 --- a/src/org/jetbrains/java/decompiler/code/CodeConstants.java +++ b/src/org/jetbrains/java/decompiler/code/CodeConstants.java @@ -1,31 +1,22 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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"}) public interface CodeConstants { - // ---------------------------------------------------------------------- // BYTECODE VERSIONS // ---------------------------------------------------------------------- - int BYTECODE_JAVA_LE_4 = 1; - int BYTECODE_JAVA_5 = 2; - int BYTECODE_JAVA_6 = 3; - int BYTECODE_JAVA_7 = 4; - int BYTECODE_JAVA_8 = 5; + int BYTECODE_JAVA_LE_4 = 48; + int BYTECODE_JAVA_5 = 49; + int BYTECODE_JAVA_6 = 50; + int BYTECODE_JAVA_7 = 51; + int BYTECODE_JAVA_8 = 52; + int BYTECODE_JAVA_9 = 53; + int BYTECODE_JAVA_10 = 54; + int BYTECODE_JAVA_11 = 55; + int BYTECODE_JAVA_12 = 56; + int BYTECODE_JAVA_13 = 57; // ---------------------------------------------------------------------- // VARIABLE TYPES @@ -63,20 +54,6 @@ public interface CodeConstants { int TYPE_FAMILY_DOUBLE = 5; int TYPE_FAMILY_OBJECT = 6; - // ---------------------------------------------------------------------- - // MODULE CONSTANTS - // ---------------------------------------------------------------------- - - int STACKSIZE_SIMPLE = 1; - int STACKSIZE_DOUBLE = 2; - - int VAR_LOCAL = 0; - int VAR_STACK = 1; - - int VAR_WRITE = 0; - int VAR_READ = 1; - - // ---------------------------------------------------------------------- // ACCESS FLAGS // ---------------------------------------------------------------------- @@ -87,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; @@ -97,6 +75,8 @@ public interface CodeConstants { int ACC_SYNTHETIC = 0x1000; int ACC_ANNOTATION = 0x2000; int ACC_ENUM = 0x4000; + int ACC_MANDATED = 0x8000; + int ACC_MODULE = 0x8000; // ---------------------------------------------------------------------- // CLASS FLAGS @@ -105,17 +85,6 @@ public interface CodeConstants { int ACC_SUPER = 0x0020; int ACC_INTERFACE = 0x0200; - - // ---------------------------------------------------------------------- - // DEPENDENCY CONSTANTS - // ---------------------------------------------------------------------- - - int DEP_CONSTANT = 0; - int DEP_UNKNOWN = 1; - int DEP_GENERAL = 2; - int DEP_PARAMS = 4; - int DEP_STATIC = 8; - // ---------------------------------------------------------------------- // INSTRUCTION GROUPS // ---------------------------------------------------------------------- @@ -145,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 @@ -351,7 +322,6 @@ public interface CodeConstants { int opc_invokestatic = 184; int opc_invokeinterface = 185; int opc_invokedynamic = 186; - int opc_xxxunusedxxx = 186; int opc_new = 187; int opc_newarray = 188; int opc_anewarray = 189; @@ -367,4 +337,7 @@ public interface CodeConstants { int opc_ifnonnull = 199; int opc_goto_w = 200; int opc_jsr_w = 201; -} + + String CLINIT_NAME = ""; + String INIT_NAME = ""; +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/code/ConstantsUtil.java b/src/org/jetbrains/java/decompiler/code/ConstantsUtil.java deleted file mode 100644 index 79eecdf..0000000 --- a/src/org/jetbrains/java/decompiler/code/ConstantsUtil.java +++ /dev/null @@ -1,482 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code; - -import org.jetbrains.java.decompiler.code.optinstructions.*; - -public class ConstantsUtil { - - public static String getName(int opcode) { - return opcodeNames[opcode]; - } - - public static Instruction getInstructionInstance(int opcode, boolean wide, int group, int bytecode_version, int[] operands) { - - Instruction instr = getInstructionInstance(opcode, bytecode_version); - instr.wide = wide; - instr.group = group; - instr.bytecode_version = bytecode_version; - instr.setOperands(operands); - - return instr; - } - - private static Instruction getInstructionInstance(int opcode, int bytecode_version) { - try { - Instruction instr; - - if ((opcode >= CodeConstants.opc_ifeq && - opcode <= CodeConstants.opc_if_acmpne) || - opcode == CodeConstants.opc_ifnull || - opcode == CodeConstants.opc_ifnonnull) { - instr = new IfInstruction(); - } - else { - - Class cl = opcodeClasses[opcode]; - - if (opcode == CodeConstants.opc_invokedynamic && bytecode_version < CodeConstants.BYTECODE_JAVA_7) { - cl = null; // instruction unused in Java 6 and before - } - - if (cl == null) { - instr = new Instruction(); - } - else { - instr = (Instruction)cl.newInstance(); - } - } - - instr.opcode = opcode; - return instr; - } - catch (Exception ex) { - return null; - } - } - - - private static String[] opcodeNames = { - "nop", // "nop", - "aconst_null", // "aconst_null", - "iconst_m1", // "iconst_m1", - "iconst_0", // "iconst_0", - "iconst_1", // "iconst_1", - "iconst_2", // "iconst_2", - "iconst_3", // "iconst_3", - "iconst_4", // "iconst_4", - "iconst_5", // "iconst_5", - "lconst_0", // "lconst_0", - "lconst_1", // "lconst_1", - "fconst_0", // "fconst_0", - "fconst_1", // "fconst_1", - "fconst_2", // "fconst_2", - "dconst_0", // "dconst_0", - "dconst_1", // "dconst_1", - "bipush", // "bipush", - "sipush", // "sipush", - "ldc", // "ldc", - "ldc_w", // "ldc_w", - "ldc2_w", // "ldc2_w", - "iload", // "iload", - "lload", // "lload", - "fload", // "fload", - "dload", // "dload", - "aload", // "aload", - "iload_0", // "iload_0", - "iload_1", // "iload_1", - "iload_2", // "iload_2", - "iload_3", // "iload_3", - "lload_0", // "lload_0", - "lload_1", // "lload_1", - "lload_2", // "lload_2", - "lload_3", // "lload_3", - "fload_0", // "fload_0", - "fload_1", // "fload_1", - "fload_2", // "fload_2", - "fload_3", // "fload_3", - "dload_0", // "dload_0", - "dload_1", // "dload_1", - "dload_2", // "dload_2", - "dload_3", // "dload_3", - "aload_0", // "aload_0", - "aload_1", // "aload_1", - "aload_2", // "aload_2", - "aload_3", // "aload_3", - "iaload", // "iaload", - "laload", // "laload", - "faload", // "faload", - "daload", // "daload", - "aaload", // "aaload", - "baload", // "baload", - "caload", // "caload", - "saload", // "saload", - "istore", // "istore", - "lstore", // "lstore", - "fstore", // "fstore", - "dstore", // "dstore", - "astore", // "astore", - "istore_0", // "istore_0", - "istore_1", // "istore_1", - "istore_2", // "istore_2", - "istore_3", // "istore_3", - "lstore_0", // "lstore_0", - "lstore_1", // "lstore_1", - "lstore_2", // "lstore_2", - "lstore_3", // "lstore_3", - "fstore_0", // "fstore_0", - "fstore_1", // "fstore_1", - "fstore_2", // "fstore_2", - "fstore_3", // "fstore_3", - "dstore_0", // "dstore_0", - "dstore_1", // "dstore_1", - "dstore_2", // "dstore_2", - "dstore_3", // "dstore_3", - "astore_0", // "astore_0", - "astore_1", // "astore_1", - "astore_2", // "astore_2", - "astore_3", // "astore_3", - "iastore", // "iastore", - "lastore", // "lastore", - "fastore", // "fastore", - "dastore", // "dastore", - "aastore", // "aastore", - "bastore", // "bastore", - "castore", // "castore", - "sastore", // "sastore", - "pop", // "pop", - "pop2", // "pop2", - "dup", // "dup", - "dup_x1", // "dup_x1", - "dup_x2", // "dup_x2", - "dup2", // "dup2", - "dup2_x1", // "dup2_x1", - "dup2_x2", // "dup2_x2", - "swap", // "swap", - "iadd", // "iadd", - "ladd", // "ladd", - "fadd", // "fadd", - "dadd", // "dadd", - "isub", // "isub", - "lsub", // "lsub", - "fsub", // "fsub", - "dsub", // "dsub", - "imul", // "imul", - "lmul", // "lmul", - "fmul", // "fmul", - "dmul", // "dmul", - "idiv", // "idiv", - "ldiv", // "ldiv", - "fdiv", // "fdiv", - "ddiv", // "ddiv", - "irem", // "irem", - "lrem", // "lrem", - "frem", // "frem", - "drem", // "drem", - "ineg", // "ineg", - "lneg", // "lneg", - "fneg", // "fneg", - "dneg", // "dneg", - "ishl", // "ishl", - "lshl", // "lshl", - "ishr", // "ishr", - "lshr", // "lshr", - "iushr", // "iushr", - "lushr", // "lushr", - "iand", // "iand", - "land", // "land", - "ior", // "ior", - "lor", // "lor", - "ixor", // "ixor", - "lxor", // "lxor", - "iinc", // "iinc", - "i2l", // "i2l", - "i2f", // "i2f", - "i2d", // "i2d", - "l2i", // "l2i", - "l2f", // "l2f", - "l2d", // "l2d", - "f2i", // "f2i", - "f2l", // "f2l", - "f2d", // "f2d", - "d2i", // "d2i", - "d2l", // "d2l", - "d2f", // "d2f", - "i2b", // "i2b", - "i2c", // "i2c", - "i2s", // "i2s", - "lcmp", // "lcmp", - "fcmpl", // "fcmpl", - "fcmpg", // "fcmpg", - "dcmpl", // "dcmpl", - "dcmpg", // "dcmpg", - "ifeq", // "ifeq", - "ifne", // "ifne", - "iflt", // "iflt", - "ifge", // "ifge", - "ifgt", // "ifgt", - "ifle", // "ifle", - "if_icmpeq", // "if_icmpeq", - "if_icmpne", // "if_icmpne", - "if_icmplt", // "if_icmplt", - "if_icmpge", // "if_icmpge", - "if_icmpgt", // "if_icmpgt", - "if_icmple", // "if_icmple", - "if_acmpeq", // "if_acmpeq", - "if_acmpne", // "if_acmpne", - "goto", // "goto", - "jsr", // "jsr", - "ret", // "ret", - "tableswitch", // "tableswitch", - "lookupswitch", // "lookupswitch", - "ireturn", // "ireturn", - "lreturn", // "lreturn", - "freturn", // "freturn", - "dreturn", // "dreturn", - "areturn", // "areturn", - "return", // "return", - "getstatic", // "getstatic", - "putstatic", // "putstatic", - "getfield", // "getfield", - "putfield", // "putfield", - "invokevirtual", // "invokevirtual", - "invokespecial", // "invokespecial", - "invokestatic", // "invokestatic", - "invokeinterface", // "invokeinterface", - //"xxxunusedxxx", // "xxxunusedxxx", Java 6 and before - "invokedynamic", // "invokedynamic", Java 7 and later - "new", // "new", - "newarray", // "newarray", - "anewarray", // "anewarray", - "arraylength", // "arraylength", - "athrow", // "athrow", - "checkcast", // "checkcast", - "instanceof", // "instanceof", - "monitorenter", // "monitorenter", - "monitorexit", // "monitorexit", - "wide", // "wide", - "multianewarray", // "multianewarray", - "ifnull", // "ifnull", - "ifnonnull", // "ifnonnull", - "goto_w", // "goto_w", - "jsr_w" // "jsr_w" - }; - - private static Class[] opcodeClasses = { - null, // "nop", - null, // "aconst_null", - null, // "iconst_m1", - null, // "iconst_0", - null, // "iconst_1", - null, // "iconst_2", - null, // "iconst_3", - null, // "iconst_4", - null, // "iconst_5", - null, // "lconst_0", - null, // "lconst_1", - null, // "fconst_0", - null, // "fconst_1", - null, // "fconst_2", - null, // "dconst_0", - null, // "dconst_1", - BIPUSH.class, // "bipush", - SIPUSH.class, // "sipush", - LDC.class, // "ldc", - LDC_W.class, // "ldc_w", - LDC2_W.class, // "ldc2_w", - ILOAD.class, // "iload", - LLOAD.class, // "lload", - FLOAD.class, // "fload", - DLOAD.class, // "dload", - ALOAD.class, // "aload", - null, // "iload_0", - null, // "iload_1", - null, // "iload_2", - null, // "iload_3", - null, // "lload_0", - null, // "lload_1", - null, // "lload_2", - null, // "lload_3", - null, // "fload_0", - null, // "fload_1", - null, // "fload_2", - null, // "fload_3", - null, // "dload_0", - null, // "dload_1", - null, // "dload_2", - null, // "dload_3", - null, // "aload_0", - null, // "aload_1", - null, // "aload_2", - null, // "aload_3", - null, // "iaload", - null, // "laload", - null, // "faload", - null, // "daload", - null, // "aaload", - null, // "baload", - null, // "caload", - null, // "saload", - ISTORE.class, // "istore", - LSTORE.class, // "lstore", - FSTORE.class, // "fstore", - DSTORE.class, // "dstore", - ASTORE.class, // "astore", - null, // "istore_0", - null, // "istore_1", - null, // "istore_2", - null, // "istore_3", - null, // "lstore_0", - null, // "lstore_1", - null, // "lstore_2", - null, // "lstore_3", - null, // "fstore_0", - null, // "fstore_1", - null, // "fstore_2", - null, // "fstore_3", - null, // "dstore_0", - null, // "dstore_1", - null, // "dstore_2", - null, // "dstore_3", - null, // "astore_0", - null, // "astore_1", - null, // "astore_2", - null, // "astore_3", - null, // "iastore", - null, // "lastore", - null, // "fastore", - null, // "dastore", - null, // "aastore", - null, // "bastore", - null, // "castore", - null, // "sastore", - null, // "pop", - null, // "pop2", - null, // "dup", - null, // "dup_x1", - null, // "dup_x2", - null, // "dup2", - null, // "dup2_x1", - null, // "dup2_x2", - null, // "swap", - null, // "iadd", - null, // "ladd", - null, // "fadd", - null, // "dadd", - null, // "isub", - null, // "lsub", - null, // "fsub", - null, // "dsub", - null, // "imul", - null, // "lmul", - null, // "fmul", - null, // "dmul", - null, // "idiv", - null, // "ldiv", - null, // "fdiv", - null, // "ddiv", - null, // "irem", - null, // "lrem", - null, // "frem", - null, // "drem", - null, // "ineg", - null, // "lneg", - null, // "fneg", - null, // "dneg", - null, // "ishl", - null, // "lshl", - null, // "ishr", - null, // "lshr", - null, // "iushr", - null, // "lushr", - null, // "iand", - null, // "land", - null, // "ior", - null, // "lor", - null, // "ixor", - null, // "lxor", - IINC.class, // "iinc", - null, // "i2l", - null, // "i2f", - null, // "i2d", - null, // "l2i", - null, // "l2f", - null, // "l2d", - null, // "f2i", - null, // "f2l", - null, // "f2d", - null, // "d2i", - null, // "d2l", - null, // "d2f", - null, // "i2b", - null, // "i2c", - null, // "i2s", - null, // "lcmp", - null, // "fcmpl", - null, // "fcmpg", - null, // "dcmpl", - null, // "dcmpg", - null, // "ifeq", - null, // "ifne", - null, // "iflt", - null, // "ifge", - null, // "ifgt", - null, // "ifle", - null, // "if_icmpeq", - null, // "if_icmpne", - null, // "if_icmplt", - null, // "if_icmpge", - null, // "if_icmpgt", - null, // "if_icmple", - null, // "if_acmpeq", - null, // "if_acmpne", - GOTO.class, // "goto", - JSR.class, // "jsr", - RET.class, // "ret", - TABLESWITCH.class, // "tableswitch", - LOOKUPSWITCH.class, // "lookupswitch", - null, // "ireturn", - null, // "lreturn", - null, // "freturn", - null, // "dreturn", - null, // "areturn", - null, // "return", - GETSTATIC.class, // "getstatic", - PUTSTATIC.class, // "putstatic", - GETFIELD.class, // "getfield", - PUTFIELD.class, // "putfield", - INVOKEVIRTUAL.class, // "invokevirtual", - INVOKESPECIAL.class, // "invokespecial", - INVOKESTATIC.class, // "invokestatic", - INVOKEINTERFACE.class, // "invokeinterface", - INVOKEDYNAMIC.class, // "xxxunusedxxx" Java 6 and before, "invokedynamic" Java 7 and later - NEW.class, // "new", - NEWARRAY.class, // "newarray", - ANEWARRAY.class, // "anewarray", - null, // "arraylength", - null, // "athrow", - CHECKCAST.class, // "checkcast", - INSTANCEOF.class, // "instanceof", - null, // "monitorenter", - null, // "monitorexit", - null, // "wide", - MULTIANEWARRAY.class, // "multianewarray", - null, // "ifnull", - null, // "ifnonnull", - GOTO_W.class, // "goto_w", - JSR_W.class // "jsr_w" - }; -} diff --git a/src/org/jetbrains/java/decompiler/code/ExceptionHandler.java b/src/org/jetbrains/java/decompiler/code/ExceptionHandler.java index 2927294..0d20726 100644 --- a/src/org/jetbrains/java/decompiler/code/ExceptionHandler.java +++ b/src/org/jetbrains/java/decompiler/code/ExceptionHandler.java @@ -1,27 +1,9 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code; import org.jetbrains.java.decompiler.main.DecompilerContext; -import java.io.DataOutputStream; -import java.io.IOException; - public class ExceptionHandler { - public int from = 0; public int to = 0; public int handler = 0; @@ -30,32 +12,12 @@ public class ExceptionHandler { public int to_instr = 0; public int handler_instr = 0; - public int class_index = 0; public String exceptionClass = null; - public ExceptionHandler() { - } - - public ExceptionHandler(int from_raw, int to_raw, int handler_raw, String exceptionClass) { - this.from = from_raw; - this.to = to_raw; - this.handler = handler_raw; - this.exceptionClass = exceptionClass; - } - - public void writeToStream(DataOutputStream out) throws IOException { - out.writeShort(from); - out.writeShort(to); - out.writeShort(handler); - out.writeShort(class_index); - } - public String toString() { - String new_line_separator = DecompilerContext.getNewLineSeparator(); - return "from: " + from + " to: " + to + " handler: " + handler + new_line_separator + "from_instr: " + from_instr + " to_instr: " + to_instr + " handler_instr: " + handler_instr + new_line_separator + "exceptionClass: " + exceptionClass + new_line_separator; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/code/ExceptionTable.java b/src/org/jetbrains/java/decompiler/code/ExceptionTable.java index e828a7f..7416623 100644 --- a/src/org/jetbrains/java/decompiler/code/ExceptionTable.java +++ b/src/org/jetbrains/java/decompiler/code/ExceptionTable.java @@ -1,58 +1,19 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code; -import org.jetbrains.java.decompiler.code.interpreter.Util; -import org.jetbrains.java.decompiler.struct.StructContext; - -import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class ExceptionTable { + public static final ExceptionTable EMPTY = new ExceptionTable(Collections.emptyList()); - private List handlers = new ArrayList(); - - public ExceptionTable() { - } + private final List handlers; public ExceptionTable(List handlers) { this.handlers = handlers; } - - public ExceptionHandler getHandlerByClass(StructContext context, int line, String valclass, boolean withany) { - - ExceptionHandler res = null; // no handler found - - for (ExceptionHandler handler : handlers) { - if (handler.from <= line && handler.to > line) { - String name = handler.exceptionClass; - - if ((withany && name == null) || // any -> finally or synchronized handler - (name != null && Util.instanceOf(context, valclass, name))) { - res = handler; - break; - } - } - } - - return res; - } - public List getHandlers() { return handlers; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java b/src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java index eb8f819..b568a24 100644 --- a/src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java +++ b/src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code; import org.jetbrains.java.decompiler.util.VBStyleCollection; @@ -25,7 +11,7 @@ public class FullInstructionSequence extends InstructionSequence { // ***************************************************************************** public FullInstructionSequence(VBStyleCollection collinstr, ExceptionTable extable) { - this.collinstr = collinstr; + super(collinstr); this.exceptionTable = extable; // translate raw exception handlers to instr diff --git a/src/org/jetbrains/java/decompiler/code/IfInstruction.java b/src/org/jetbrains/java/decompiler/code/IfInstruction.java deleted file mode 100644 index 1d4bf6c..0000000 --- a/src/org/jetbrains/java/decompiler/code/IfInstruction.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code; - -import java.io.DataOutputStream; -import java.io.IOException; - -/* - * opc_ifeq, opc_ifne, opc_iflt, opc_ifge, opc_ifgt, opc_ifle, opc_if_icmpeq, opc_if_icmpne, opc_if_icmplt, - * opc_if_icmpge, opc_if_icmpgt, opc_if_icmple, opc_if_acmpeq, opc_if_acmpne, opc_ifnull, opc_ifnonnull - */ - -public class IfInstruction extends JumpInstruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opcode); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/Instruction.java b/src/org/jetbrains/java/decompiler/code/Instruction.java index d77f3b4..1a260f2 100644 --- a/src/org/jetbrains/java/decompiler/code/Instruction.java +++ b/src/org/jetbrains/java/decompiler/code/Instruction.java @@ -1,126 +1,87 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code; -import java.io.DataOutputStream; -import java.io.IOException; +import org.jetbrains.java.decompiler.util.TextUtil; public class Instruction implements CodeConstants { - - // ***************************************************************************** - // public fields - // ***************************************************************************** - - public int opcode; - - public int group = CodeConstants.GROUP_GENERAL; - - public boolean wide = false; - - public int bytecode_version = BYTECODE_JAVA_LE_4; - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private int[] operands = null; - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public Instruction() { + public static Instruction create(int opcode, boolean wide, int group, int bytecodeVersion, int[] operands) { + if (opcode >= opc_ifeq && opcode <= opc_if_acmpne || + opcode == opc_ifnull || opcode == opc_ifnonnull || + opcode == opc_jsr || opcode == opc_jsr_w || + opcode == opc_goto || opcode == opc_goto_w) { + return new JumpInstruction(opcode, group, wide, bytecodeVersion, operands); + } + else if (opcode == opc_tableswitch || opcode == opc_lookupswitch) { + return new SwitchInstruction(opcode, group, wide, bytecodeVersion, operands); + } + else { + return new Instruction(opcode, group, wide, bytecodeVersion, operands); + } } - public int length() { - return 1; + public static boolean equals(Instruction i1, Instruction i2) { + return i1 != null && i2 != null && + (i1 == i2 || + i1.opcode == i2.opcode && + i1.wide == i2.wide && + i1.operandsCount() == i2.operandsCount()); } + public final int opcode; + public final int group; + public final boolean wide; + public final int bytecodeVersion; + + protected final int[] operands; + + public Instruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) { + this.opcode = opcode; + this.group = group; + this.wide = wide; + this.bytecodeVersion = bytecodeVersion; + this.operands = operands; + } + + public void initInstruction(InstructionSequence seq) { } + public int operandsCount() { - return (operands == null) ? 0 : operands.length; + return operands == null ? 0 : operands.length; } - public int getOperand(int index) { + public int operand(int index) { return operands[index]; } - public Instruction clone() { - return ConstantsUtil.getInstructionInstance(opcode, wide, group, bytecode_version, operands == null ? null : operands.clone()); + public boolean canFallThrough() { + return opcode != opc_goto && opcode != opc_goto_w && opcode != opc_ret && + !(opcode >= opc_ireturn && opcode <= opc_return) && + opcode != opc_athrow && + opcode != opc_jsr && opcode != opc_tableswitch && opcode != opc_lookupswitch; } + @Override public String toString() { - - String res = wide ? "@wide " : ""; - res += "@" + ConstantsUtil.getName(opcode); + StringBuilder res = new StringBuilder(); + if (wide) res.append("@wide "); + res.append("@").append(TextUtil.getInstructionName(opcode)); int len = operandsCount(); for (int i = 0; i < len; i++) { int op = operands[i]; if (op < 0) { - res += " -" + Integer.toHexString(-op); + res.append(" -").append(Integer.toHexString(-op)); } else { - res += " " + Integer.toHexString(op); + res.append(" ").append(Integer.toHexString(op)); } } - return res; + return res.toString(); } - public boolean canFallthrough() { - return opcode != opc_goto && opcode != opc_goto_w && opcode != opc_ret && - !(opcode >= opc_ireturn && opcode <= opc_return) && opcode != opc_athrow - && opcode != opc_jsr && opcode != opc_tableswitch && opcode != opc_lookupswitch; + @Override + @SuppressWarnings("MethodDoesntCallSuperMethod") + public Instruction clone() { + return create(opcode, wide, group, bytecodeVersion, operands == null ? null : operands.clone()); } - - public boolean equalsInstruction(Instruction instr) { - if (opcode != instr.opcode || wide != instr.wide - || operandsCount() != instr.operandsCount()) { - return false; - } - - if (operands != null) { - for (int i = 0; i < operands.length; i++) { - if (operands[i] != instr.getOperand(i)) { - return false; - } - } - } - - return true; - } - - // should be overwritten by subclasses - public void initInstruction(InstructionSequence seq) { - } - - // should be overwritten by subclasses - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opcode); - } - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public int[] getOperands() { - return operands; - } - - public void setOperands(int[] operands) { - this.operands = operands; - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/code/InstructionSequence.java b/src/org/jetbrains/java/decompiler/code/InstructionSequence.java index 709a85d..3d0cab0 100644 --- a/src/org/jetbrains/java/decompiler/code/InstructionSequence.java +++ b/src/org/jetbrains/java/decompiler/code/InstructionSequence.java @@ -1,50 +1,36 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code; -import org.jetbrains.java.decompiler.code.interpreter.Util; import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.struct.StructContext; -import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - - public abstract class InstructionSequence { // ***************************************************************************** // private fields // ***************************************************************************** - protected VBStyleCollection collinstr = new VBStyleCollection(); + protected final VBStyleCollection collinstr; protected int pointer = 0; - protected ExceptionTable exceptionTable = new ExceptionTable(); + protected ExceptionTable exceptionTable = ExceptionTable.EMPTY; + + protected InstructionSequence() { + this(new VBStyleCollection<>()); + } + + protected InstructionSequence(VBStyleCollection collinstr) { + this.collinstr = collinstr; + } // ***************************************************************************** // public methods // ***************************************************************************** // to nbe overwritten + @Override public InstructionSequence clone() { return null; } @@ -52,7 +38,7 @@ public abstract class InstructionSequence { public void clear() { collinstr.clear(); pointer = 0; - exceptionTable = new ExceptionTable(); + exceptionTable = ExceptionTable.EMPTY; } public void addInstruction(Instruction inst, int offset) { @@ -73,8 +59,10 @@ public abstract class InstructionSequence { collinstr.remove(index); } - public Instruction getCurrentInstr() { - return collinstr.get(pointer); + public void removeLast() { + if (!collinstr.isEmpty()) { + collinstr.remove(collinstr.size() - 1); + } } public Instruction getInstr(int index) { @@ -85,16 +73,12 @@ public abstract class InstructionSequence { return collinstr.getLast(); } - public int getCurrentOffset() { - return collinstr.getKey(pointer).intValue(); - } - -public int getOffset(int index) { - return collinstr.getKey(index).intValue(); + public int getOffset(int index) { + return collinstr.getKey(index); } public int getPointerByAbsOffset(int offset) { - Integer absoffset = new Integer(offset); + Integer absoffset = offset; if (collinstr.containsKey(absoffset)) { return collinstr.getIndexByKey(absoffset); } @@ -104,7 +88,7 @@ public int getOffset(int index) { } public int getPointerByRelOffset(int offset) { - Integer absoffset = new Integer(collinstr.getKey(pointer).intValue() + offset); + Integer absoffset = collinstr.getKey(pointer) + offset; if (collinstr.containsKey(absoffset)) { return collinstr.getIndexByKey(absoffset); } @@ -113,13 +97,6 @@ public int getOffset(int index) { } } - public void setPointerByAbsOffset(int offset) { - Integer absoffset = new Integer(collinstr.getKey(pointer).intValue() + offset); - if (collinstr.containsKey(absoffset)) { - pointer = collinstr.getIndexByKey(absoffset); - } - } - public int length() { return collinstr.size(); } @@ -143,7 +120,7 @@ public int getOffset(int index) { StringBuilder buf = new StringBuilder(); for (int i = 0; i < collinstr.size(); i++) { - buf.append(InterpreterUtil.getIndentString(indent)); + buf.append(TextUtil.getIndentString(indent)); buf.append(collinstr.getKey(i).intValue()); buf.append(": "); buf.append(collinstr.get(i).toString()); @@ -153,58 +130,6 @@ public int getOffset(int index) { return buf.toString(); } - public void writeCodeToStream(DataOutputStream out) throws IOException { - - for (int i = 0; i < collinstr.size(); i++) { - collinstr.get(i).writeToStream(out, collinstr.getKey(i).intValue()); - } - } - - public void writeExceptionsToStream(DataOutputStream out) throws IOException { - - List handlers = exceptionTable.getHandlers(); - - out.writeShort(handlers.size()); - for (int i = 0; i < handlers.size(); i++) { - handlers.get(i).writeToStream(out); - } - } - - public void sortHandlers(final StructContext context) { - - Collections.sort(exceptionTable.getHandlers(), new Comparator() { - - public int compare(ExceptionHandler handler0, ExceptionHandler handler1) { - - if (handler0.to == handler1.to) { - if (handler0.exceptionClass == null) { - return 1; - } - else { - if (handler1.exceptionClass == null) { - return -1; - } - else if (handler0.exceptionClass.equals(handler1.exceptionClass)) { - return (handler0.from > handler1.from) ? -1 : 1; // invalid code - } - else { - if (Util.instanceOf(context, handler0.exceptionClass, handler1.exceptionClass)) { - return -1; - } - else { - return 1; - } - } - } - } - else { - return (handler0.to > handler1.to) ? 1 : -1; - } - } - }); - } - - // ***************************************************************************** // getter and setter methods // ***************************************************************************** @@ -220,8 +145,4 @@ public int getOffset(int index) { public ExceptionTable getExceptionTable() { return exceptionTable; } - - public void setExceptionTable(ExceptionTable exceptionTable) { - this.exceptionTable = exceptionTable; - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/code/JumpInstruction.java b/src/org/jetbrains/java/decompiler/code/JumpInstruction.java index 44f4ff9..129df62 100644 --- a/src/org/jetbrains/java/decompiler/code/JumpInstruction.java +++ b/src/org/jetbrains/java/decompiler/code/JumpInstruction.java @@ -1,42 +1,22 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code; -/* - * opc_ifeq, opc_ifne, opc_iflt, opc_ifge, opc_ifgt, opc_ifle, opc_if_icmpeq, opc_if_icmpne, opc_if_icmplt, - * opc_if_icmpge, opc_if_icmpgt, opc_if_icmple, opc_if_acmpeq, opc_if_acmpne, opc_ifnull, opc_ifnonnull - * opc_goto, opc_jsr, opc_goto_w, opc_jsr_w - */ - - public class JumpInstruction extends Instruction { - public int destination; - public JumpInstruction() { + public JumpInstruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) { + super(opcode, group, wide, bytecodeVersion, operands); } + @Override public void initInstruction(InstructionSequence seq) { - destination = seq.getPointerByRelOffset(this.getOperand(0)); + destination = seq.getPointerByRelOffset(this.operand(0)); } + @Override public JumpInstruction clone() { - JumpInstruction newinstr = (JumpInstruction)super.clone(); - - newinstr.destination = destination; - return newinstr; + JumpInstruction copy = (JumpInstruction)super.clone(); + copy.destination = destination; + return copy; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java b/src/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java index bcc3c4a..0b62c51 100644 --- a/src/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java +++ b/src/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code; import org.jetbrains.java.decompiler.util.VBStyleCollection; @@ -23,17 +9,14 @@ public class SimpleInstructionSequence extends InstructionSequence { } public SimpleInstructionSequence(VBStyleCollection collinstr) { - this.collinstr = collinstr; + super(collinstr); } + @Override public SimpleInstructionSequence clone() { SimpleInstructionSequence newseq = new SimpleInstructionSequence(collinstr.clone()); newseq.setPointer(this.getPointer()); return newseq; } - - public void removeInstruction(int index) { - collinstr.remove(index); - } } diff --git a/src/org/jetbrains/java/decompiler/code/SwitchInstruction.java b/src/org/jetbrains/java/decompiler/code/SwitchInstruction.java index 6aa6a7f..61810d9 100644 --- a/src/org/jetbrains/java/decompiler/code/SwitchInstruction.java +++ b/src/org/jetbrains/java/decompiler/code/SwitchInstruction.java @@ -1,97 +1,61 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code; -/* - * opc_tableswitch, lookupswitch - */ - public class SwitchInstruction extends Instruction { - private int[] destinations; - private int[] values; + private int defaultDestination; - private int defaultdest; - - public SwitchInstruction() { + public SwitchInstruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) { + super(opcode, group, wide, bytecodeVersion, operands); } - + @Override public void initInstruction(InstructionSequence seq) { + defaultDestination = seq.getPointerByRelOffset(operands[0]); - int pref = (opcode == CodeConstants.opc_tableswitch ? 3 : 2); - int len = this.getOperands().length - pref; - defaultdest = seq.getPointerByRelOffset(this.getOperand(0)); - + int prefix = opcode == CodeConstants.opc_tableswitch ? 3 : 2; + int len = operands.length - prefix; int low = 0; - if (opcode == CodeConstants.opc_lookupswitch) { len /= 2; } else { - low = this.getOperand(1); + low = operands[1]; } destinations = new int[len]; values = new int[len]; - for (int i = 0, k = 0; i < len; i++, k++) { if (opcode == CodeConstants.opc_lookupswitch) { - values[i] = this.getOperand(pref + k); + values[i] = operands[prefix + k]; k++; } else { values[i] = low + k; } - destinations[i] = seq.getPointerByRelOffset(this.getOperand(pref + k)); + destinations[i] = seq.getPointerByRelOffset(operands[prefix + k]); } } - public SwitchInstruction clone() { - SwitchInstruction newinstr = (SwitchInstruction)super.clone(); - - newinstr.defaultdest = defaultdest; - newinstr.destinations = destinations.clone(); - newinstr.values = values.clone(); - - return newinstr; - } - public int[] getDestinations() { return destinations; } - public void setDestinations(int[] destinations) { - this.destinations = destinations; - } - - public int getDefaultdest() { - return defaultdest; - } - - public void setDefaultdest(int defaultdest) { - this.defaultdest = defaultdest; - } - public int[] getValues() { return values; } - public void setValues(int[] values) { - this.values = values; + public int getDefaultDestination() { + return defaultDestination; } -} + + @Override + public SwitchInstruction clone() { + SwitchInstruction copy = (SwitchInstruction)super.clone(); + copy.defaultDestination = defaultDestination; + copy.destinations = destinations.clone(); + copy.values = values.clone(); + return copy; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java b/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java index 28e0ae8..265ffca 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.code.cfg; import org.jetbrains.java.decompiler.code.Instruction; @@ -30,8 +16,7 @@ public class BasicBlock implements IGraphNode { // public fields // ***************************************************************************** - public int id = 0; - + public int id; public int mark = 0; // ***************************************************************************** @@ -40,19 +25,11 @@ public class BasicBlock implements IGraphNode { private InstructionSequence seq = new SimpleInstructionSequence(); - private List preds = new ArrayList(); - - private List succs = new ArrayList(); - - private List instrOldOffsets = new ArrayList(); - - private List predExceptions = new ArrayList(); - - private List succExceptions = new ArrayList(); - - - public BasicBlock() { - } + private final List preds = new ArrayList<>(); + private final List succs = new ArrayList<>(); + private final List instrOldOffsets = new ArrayList<>(); + private final List predExceptions = new ArrayList<>(); + private final List succExceptions = new ArrayList<>(); public BasicBlock(int id) { this.id = id; @@ -62,24 +39,17 @@ public class BasicBlock implements IGraphNode { // public methods // ***************************************************************************** - public Object clone() { + @Override + @SuppressWarnings("MethodDoesntCallSuperMethod") + public BasicBlock clone() { + BasicBlock block = new BasicBlock(id); - BasicBlock block = new BasicBlock(); - block.id = id; block.setSeq(seq.clone()); - block.setInstrOldOffsets(new ArrayList(instrOldOffsets)); + block.instrOldOffsets.addAll(instrOldOffsets); return block; } - public void free() { - preds.clear(); - succs.clear(); - instrOldOffsets.clear(); - succExceptions.clear(); - seq = new SimpleInstructionSequence(); - } - public Instruction getInstruction(int index) { return seq.getInstr(index); } @@ -110,7 +80,7 @@ public class BasicBlock implements IGraphNode { } public void removePredecessor(BasicBlock block) { - while (preds.remove(block)) ; + while (preds.remove(block)) /**/; } public void addSuccessor(BasicBlock block) { @@ -119,11 +89,11 @@ public class BasicBlock implements IGraphNode { } public void removeSuccessor(BasicBlock block) { - while (succs.remove(block)) ; + while (succs.remove(block)) /**/; block.removePredecessor(this); } - // FIXME: unify block comparisons: id or direkt equality + // FIXME: unify block comparisons: id or direct equality public void replaceSuccessor(BasicBlock oldBlock, BasicBlock newBlock) { for (int i = 0; i < succs.size(); i++) { if (succs.get(i).id == oldBlock.id) { @@ -147,7 +117,7 @@ public class BasicBlock implements IGraphNode { } public void removePredecessorException(BasicBlock block) { - while (predExceptions.remove(block)) ; + while (predExceptions.remove(block)) /**/; } public void addSuccessorException(BasicBlock block) { @@ -158,7 +128,7 @@ public class BasicBlock implements IGraphNode { } public void removeSuccessorException(BasicBlock block) { - while (succExceptions.remove(block)) ; + while (succExceptions.remove(block)) /**/; block.removePredecessorException(this); } @@ -173,27 +143,6 @@ public class BasicBlock implements IGraphNode { return id + ":" + new_line_separator + seq.toString(indent); } - public String toStringOldIndices() { - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - StringBuilder buf = new StringBuilder(); - - for (int i = 0; i < seq.length(); i++) { - if (i < instrOldOffsets.size()) { - buf.append(instrOldOffsets.get(i)); - } - else { - buf.append("-1"); - } - buf.append(": "); - buf.append(seq.getInstr(i).toString()); - buf.append(new_line_separator); - } - - return buf.toString(); - } - public boolean isSuccessor(BasicBlock block) { for (BasicBlock succ : succs) { if (succ.id == block.id) { @@ -203,15 +152,6 @@ public class BasicBlock implements IGraphNode { return false; } - public boolean isPredecessor(BasicBlock block) { - for (int i = 0; i < preds.size(); i++) { - if (preds.get(i).id == block.id) { - return true; - } - } - return false; - } - // ***************************************************************************** // getter and setter methods // ***************************************************************************** @@ -220,12 +160,9 @@ public class BasicBlock implements IGraphNode { return instrOldOffsets; } - public void setInstrOldOffsets(List instrInds) { - this.instrOldOffsets = instrInds; - } - + @Override public List getPredecessors() { - List lst = new ArrayList(preds); + List lst = new ArrayList<>(preds); lst.addAll(predExceptions); return lst; } @@ -234,10 +171,6 @@ public class BasicBlock implements IGraphNode { return preds; } - public void setPreds(List preds) { - this.preds = preds; - } - public InstructionSequence getSeq() { return seq; } @@ -250,25 +183,11 @@ public class BasicBlock implements IGraphNode { return succs; } - public void setSuccs(List succs) { - this.succs = succs; - } - - public List getSuccExceptions() { return succExceptions; } - - public void setSuccExceptions(List succExceptions) { - this.succExceptions = succExceptions; - } - public List getPredExceptions() { return predExceptions; } - - public void setPredExceptions(List predExceptions) { - this.predExceptions = predExceptions; - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java index 44c58f0..f060fbd 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java @@ -1,24 +1,11 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -47,7 +34,7 @@ public class ControlFlowGraph implements CodeConstants { private Map subroutines; - private Set finallyExits = new HashSet(); + private final Set finallyExits = new HashSet<>(); // ***************************************************************************** // constructors @@ -62,19 +49,6 @@ public class ControlFlowGraph implements CodeConstants { // public methods // ***************************************************************************** - public void free() { - - for (BasicBlock block : blocks) { - block.free(); - } - - blocks.clear(); - first = null; - last = null; - exceptions.clear(); - finallyExits.clear(); - } - public void removeMarkers() { for (BasicBlock block : blocks) { block.mark = 0; @@ -82,6 +56,7 @@ public class ControlFlowGraph implements CodeConstants { } public String toString() { + if (blocks == null) return "Empty"; String new_line_separator = DecompilerContext.getNewLineSeparator(); @@ -93,12 +68,11 @@ public class ControlFlowGraph implements CodeConstants { buf.append("----- Edges -----").append(new_line_separator); List suc = block.getSuccs(); - for (int j = 0; j < suc.size(); j++) { - buf.append(">>>>>>>>(regular) Block ").append(suc.get(j).id).append(new_line_separator); + for (BasicBlock aSuc : suc) { + buf.append(">>>>>>>>(regular) Block ").append(aSuc.id).append(new_line_separator); } suc = block.getSuccExceptions(); - for (int j = 0; j < suc.size(); j++) { - BasicBlock handler = suc.get(j); + for (BasicBlock handler : suc) { ExceptionRangeCFG range = getExceptionRange(handler, block); if (range == null) { @@ -123,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(); @@ -169,13 +143,7 @@ public class ControlFlowGraph implements CodeConstants { } } - Iterator> it = subroutines.entrySet().iterator(); - while (it.hasNext()) { - Entry ent = it.next(); - if (ent.getKey() == block || ent.getValue() == block) { - it.remove(); - } - } + subroutines.entrySet().removeIf(ent -> ent.getKey() == block || ent.getValue() == block); } public ExceptionRangeCFG getExceptionRange(BasicBlock handler, BasicBlock block) { @@ -224,7 +192,7 @@ public class ControlFlowGraph implements CodeConstants { short[] states = findStartInstructions(instrseq); - Map mapInstrBlocks = new HashMap(); + Map mapInstrBlocks = new HashMap<>(); VBStyleCollection colBlocks = createBasicBlocks(states, instrseq, mapInstrBlocks); blocks = colBlocks; @@ -243,7 +211,7 @@ public class ControlFlowGraph implements CodeConstants { int len = seq.length(); short[] inststates = new short[len]; - Set excSet = new HashSet(); + Set excSet = new HashSet<>(); for (ExceptionHandler handler : seq.getExceptionTable().getHandlers()) { excSet.add(handler.from_instr); @@ -255,7 +223,7 @@ public class ControlFlowGraph implements CodeConstants { for (int i = 0; i < len; i++) { // exception blocks - if (excSet.contains(new Integer(i))) { + if (excSet.contains(i)) { inststates[i] = 1; } @@ -274,7 +242,7 @@ public class ControlFlowGraph implements CodeConstants { for (int j = dests.length - 1; j >= 0; j--) { inststates[dests[j]] = 1; } - inststates[swinstr.getDefaultdest()] = 1; + inststates[swinstr.getDefaultDestination()] = 1; if (i + 1 < len) { inststates[i + 1] = 1; } @@ -292,10 +260,10 @@ public class ControlFlowGraph implements CodeConstants { InstructionSequence instrseq, Map mapInstrBlocks) { - VBStyleCollection col = new VBStyleCollection(); + VBStyleCollection col = new VBStyleCollection<>(); InstructionSequence currseq = null; - ArrayList lstOffs = null; + List lstOffs = null; int len = startblock.length; short counter = 0; @@ -305,14 +273,11 @@ public class ControlFlowGraph implements CodeConstants { for (int i = 0; i < len; i++) { if (startblock[i] == 1) { - currentBlock = new BasicBlock(); - currentBlock.id = ++counter; + currentBlock = new BasicBlock(++counter); - currseq = new SimpleInstructionSequence(); - lstOffs = new ArrayList(); + currseq = currentBlock.getSeq(); + lstOffs = currentBlock.getInstrOldOffsets(); - currentBlock.setSeq(currseq); - currentBlock.setInstrOldOffsets(lstOffs); col.addWithKey(currentBlock, currentBlock.id); blockoffset = instrseq.getOffset(i); @@ -338,7 +303,7 @@ public class ControlFlowGraph implements CodeConstants { BasicBlock block = lstbb.get(i); Instruction instr = block.getLastInstruction(); - boolean fallthrough = instr.canFallthrough(); + boolean fallthrough = instr.canFallThrough(); BasicBlock bTemp; switch (instr.group) { @@ -352,10 +317,10 @@ public class ControlFlowGraph implements CodeConstants { SwitchInstruction sinstr = (SwitchInstruction)instr; int[] dests = sinstr.getDestinations(); - bTemp = mapInstrBlocks.get(((SwitchInstruction)instr).getDefaultdest()); + bTemp = mapInstrBlocks.get(((SwitchInstruction)instr).getDefaultDestination()); block.addSuccessor(bTemp); - for (int j = 0; j < dests.length; j++) { - bTemp = mapInstrBlocks.get(dests[j]); + for (int dest1 : dests) { + bTemp = mapInstrBlocks.get(dest1); block.addSuccessor(bTemp); } } @@ -369,9 +334,9 @@ public class ControlFlowGraph implements CodeConstants { private void setExceptionEdges(InstructionSequence instrseq, Map instrBlocks) { - exceptions = new ArrayList(); + exceptions = new ArrayList<>(); - Map mapRanges = new HashMap(); + Map mapRanges = new HashMap<>(); for (ExceptionHandler handler : instrseq.getExceptionTable().getHandlers()) { @@ -387,7 +352,7 @@ public class ControlFlowGraph implements CodeConstants { } else { - List protectedRange = new ArrayList(); + List protectedRange = new ArrayList<>(); for (int j = from.id; j < to.id; j++) { BasicBlock block = blocks.getWithKey(j); protectedRange.add(block); @@ -396,7 +361,7 @@ public class ControlFlowGraph implements CodeConstants { ExceptionRangeCFG range = new ExceptionRangeCFG(protectedRange, handle, handler.exceptionClass == null ? null - : Arrays.asList(handler.exceptionClass)); + : Collections.singletonList(handler.exceptionClass)); mapRanges.put(key, range); exceptions.add(range); @@ -406,19 +371,19 @@ public class ControlFlowGraph implements CodeConstants { private void setSubroutineEdges() { - final Map subroutines = new HashMap(); + final Map subroutines = new LinkedHashMap<>(); for (BasicBlock block : blocks) { if (block.getSeq().getLastInstr().opcode == CodeConstants.opc_jsr) { - LinkedList stack = new LinkedList(); - LinkedList> stackJsrStacks = new LinkedList>(); + LinkedList stack = new LinkedList<>(); + LinkedList> stackJsrStacks = new LinkedList<>(); - Set setVisited = new HashSet(); + Set setVisited = new HashSet<>(); stack.add(block); - stackJsrStacks.add(new LinkedList()); + stackJsrStacks.add(new LinkedList<>()); while (!stack.isEmpty()) { @@ -451,7 +416,7 @@ public class ControlFlowGraph implements CodeConstants { for (BasicBlock succ : node.getSuccs()) { if (!setVisited.contains(succ)) { stack.add(succ); - stackJsrStacks.add(new LinkedList(jsrstack)); + stackJsrStacks.add(new LinkedList<>(jsrstack)); } } } @@ -468,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; @@ -482,7 +447,7 @@ public class ControlFlowGraph implements CodeConstants { private int processJsrRanges() { - List lstJsrAll = new ArrayList(); + List lstJsrAll = new ArrayList<>(); // get all jsr ranges for (Entry ent : subroutines.entrySet()) { @@ -494,7 +459,7 @@ public class ControlFlowGraph implements CodeConstants { // sort ranges // FIXME: better sort order - List lstJsr = new ArrayList(); + List lstJsr = new ArrayList<>(); for (JsrRecord arr : lstJsrAll) { int i = 0; for (; i < lstJsr.size(); i++) { @@ -516,7 +481,7 @@ public class ControlFlowGraph implements CodeConstants { Set set1 = arr1.range; if (!set.contains(arr1.jsr) && !set1.contains(arr.jsr)) { // rang 0 doesn't contain entry 1 and vice versa - Set setc = new HashSet(set); + Set setc = new HashSet<>(set); setc.retainAll(set1); if (!setc.isEmpty()) { @@ -532,9 +497,9 @@ public class ControlFlowGraph implements CodeConstants { private Set getJsrRange(BasicBlock jsr, BasicBlock ret) { - Set blocks = new HashSet(); + Set blocks = new HashSet<>(); - List lstNodes = new LinkedList(); + List lstNodes = new LinkedList<>(); lstNodes.add(jsr); BasicBlock dom = jsr.getSuccs().get(0); @@ -596,8 +561,8 @@ public class ControlFlowGraph implements CodeConstants { private void splitJsrRange(BasicBlock jsr, BasicBlock ret, Set common_blocks) { - List lstNodes = new LinkedList(); - Map mapNewNodes = new HashMap(); + List lstNodes = new LinkedList<>(); + Map mapNewNodes = new HashMap<>(); lstNodes.add(jsr); mapNewNodes.put(jsr.id, jsr); @@ -633,9 +598,8 @@ public class ControlFlowGraph implements CodeConstants { node.replaceSuccessor(child, mapNewNodes.get(childid)); } else if (common_blocks.contains(child)) { - // make a copy of the current block - BasicBlock copy = (BasicBlock)child.clone(); + BasicBlock copy = child.clone(); copy.id = ++last_id; // copy all successors if (copy.getLastInstruction().opcode == CodeConstants.opc_ret && @@ -682,14 +646,14 @@ public class ControlFlowGraph implements CodeConstants { ExceptionRangeCFG range = exceptions.get(i); List lstRange = range.getProtectedRange(); - HashSet setBoth = new HashSet(common_blocks); + HashSet setBoth = new HashSet<>(common_blocks); setBoth.retainAll(lstRange); if (setBoth.size() > 0) { List lstNewRange; if (setBoth.size() == lstRange.size()) { - lstNewRange = new ArrayList(); + lstNewRange = new ArrayList<>(); ExceptionRangeCFG newRange = new ExceptionRangeCFG(lstNewRange, mapNewNodes.get(range.getHandler().id), range.getExceptionTypes()); exceptions.add(newRange); @@ -705,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) { @@ -753,7 +717,7 @@ public class ControlFlowGraph implements CodeConstants { if (suc.mark != 1) { DataPoint point = new DataPoint(); - point.setLocalVariables(new ArrayList(data.getLocalVariables())); + point.setLocalVariables(new ArrayList<>(data.getLocalVariables())); point.getStack().push(new VarType(CodeConstants.TYPE_OBJECT, 0, null)); removeJsrInstructions(pool, suc, point); @@ -765,9 +729,7 @@ public class ControlFlowGraph implements CodeConstants { first = blocks.get(0); - last = new BasicBlock(); - last.id = ++last_id; - last.setSeq(new SimpleInstructionSequence()); + last = new BasicBlock(++last_id); for (BasicBlock block : blocks) { if (block.getSuccs().isEmpty()) { @@ -778,18 +740,18 @@ public class ControlFlowGraph implements CodeConstants { public List getReversePostOrder() { - List res = new LinkedList(); + List res = new LinkedList<>(); addToReversePostOrderListIterative(first, res); return res; } - private static void addToReversePostOrderListIterative(BasicBlock root, List lst) { + private static void addToReversePostOrderListIterative(BasicBlock root, List lst) { - LinkedList stackNode = new LinkedList(); - LinkedList stackIndex = new LinkedList(); + LinkedList stackNode = new LinkedList<>(); + LinkedList stackIndex = new LinkedList<>(); - Set setVisited = new HashSet(); + Set setVisited = new HashSet<>(); stackNode.add(root); stackIndex.add(0); @@ -801,7 +763,7 @@ public class ControlFlowGraph implements CodeConstants { setVisited.add(node); - List lstSuccs = new ArrayList(node.getSuccs()); + List lstSuccs = new ArrayList<>(node.getSuccs()); lstSuccs.addAll(node.getSuccExceptions()); for (; index < lstSuccs.size(); index++) { @@ -834,10 +796,6 @@ public class ControlFlowGraph implements CodeConstants { return blocks; } - public void setBlocks(VBStyleCollection blocks) { - this.blocks = blocks; - } - public BasicBlock getFirst() { return first; } @@ -846,39 +804,15 @@ public class ControlFlowGraph implements CodeConstants { this.first = first; } - public List getEndBlocks() { - return last.getPreds(); - } - public List getExceptions() { return exceptions; } - public void setExceptions(List exceptions) { - this.exceptions = exceptions; - } - public BasicBlock getLast() { return last; } - public void setLast(BasicBlock last) { - this.last = last; - } - - public Map getSubroutines() { - return subroutines; - } - - public void setSubroutines(Map subroutines) { - this.subroutines = subroutines; - } - public Set getFinallyExits() { return finallyExits; } - - public void setFinallyExits(HashSet finallyExits) { - this.finallyExits = finallyExits; - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java b/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java index 47e0e21..395d03d 100644 --- a/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java +++ b/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java @@ -1,33 +1,15 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; +import java.util.stream.Collectors; public class ExceptionRangeCFG { - - private List protectedRange = new ArrayList(); // FIXME: replace with set - + private final List protectedRange; // FIXME: replace with set private BasicBlock handler; - private List exceptionTypes; public ExceptionRangeCFG(List protectedRange, BasicBlock handler, List exceptionType) { @@ -35,7 +17,7 @@ public class ExceptionRangeCFG { this.handler = handler; if (exceptionType != null) { - this.exceptionTypes = new ArrayList(exceptionType); + this.exceptionTypes = new ArrayList<>(exceptionType); } } @@ -43,22 +25,28 @@ public class ExceptionRangeCFG { return protectedRange.contains(handler); } + @Override public String toString() { - String new_line_separator = DecompilerContext.getNewLineSeparator(); - 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); buf.append("range: "); - for (int i = 0; i < protectedRange.size(); i++) { - buf.append(protectedRange.get(i).id).append(" "); + for (BasicBlock block : protectedRange) { + buf.append(block.id).append(" "); } buf.append(new_line_separator); @@ -77,16 +65,11 @@ public class ExceptionRangeCFG { return protectedRange; } - public void setProtectedRange(List protectedRange) { - this.protectedRange = protectedRange; - } - public List getExceptionTypes() { return this.exceptionTypes; } public void addExceptionType(String exceptionType) { - if (this.exceptionTypes == null) { return; } @@ -100,30 +83,6 @@ public class ExceptionRangeCFG { } public String getUniqueExceptionsString() { - - if (exceptionTypes == null) { - return null; - } - - Set setExceptionStrings = new HashSet(); - - for (String exceptionType : exceptionTypes) { // normalize order - setExceptionStrings.add(exceptionType); - } - - String ret = ""; - for (String exception : setExceptionStrings) { - if (!ret.isEmpty()) { - ret += ":"; - } - ret += exception; - } - - return ret; + return exceptionTypes != null ? exceptionTypes.stream().distinct().collect(Collectors.joining(":")) : null; } - - - // public void setExceptionType(String exceptionType) { - // this.exceptionType = exceptionType; - // } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java b/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java index 1af23a4..aeafe5c 100644 --- a/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java +++ b/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java @@ -1,31 +1,18 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; import org.jetbrains.java.decompiler.code.Instruction; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.LinkConstant; +import org.jetbrains.java.decompiler.struct.consts.PooledConstant; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.gen.DataPoint; 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 = { @@ -338,7 +325,6 @@ public class InstructionImpact { public static void stepTypes(DataPoint data, Instruction instr, ConstantPool pool) { - ListStack stack = data.getStack(); int[][] arr = stack_impact[instr.opcode]; @@ -350,8 +336,7 @@ public class InstructionImpact { if (read != null) { int depth = 0; - for (int i = 0; i < read.length; i++) { - int type = read[i]; + for (int type : read) { depth++; if (type == CodeConstants.TYPE_LONG || type == CodeConstants.TYPE_DOUBLE) { @@ -363,8 +348,7 @@ public class InstructionImpact { } if (write != null) { - for (int i = 0; i < write.length; i++) { - int type = write[i]; + for (int type : write) { stack.push(new VarType(type)); if (type == CodeConstants.TYPE_LONG || type == CodeConstants.TYPE_DOUBLE) { @@ -394,8 +378,8 @@ public class InstructionImpact { case CodeConstants.opc_ldc: case CodeConstants.opc_ldc_w: case CodeConstants.opc_ldc2_w: - cn = pool.getPrimitiveConstant(instr.getOperand(0)); - switch (cn.type) { + PooledConstant constant = pool.getConstant(instr.operand(0)); + switch (constant.type) { case CodeConstants.CONSTANT_Integer: stack.push(new VarType(CodeConstants.TYPE_INT)); break; @@ -416,10 +400,13 @@ public class InstructionImpact { case CodeConstants.CONSTANT_Class: stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Class")); break; + case CodeConstants.CONSTANT_MethodHandle: + stack.push(new VarType(((LinkConstant)constant).descriptor)); + break; } break; case CodeConstants.opc_aload: - var1 = data.getVariable(instr.getOperand(0)); + var1 = data.getVariable(instr.operand(0)); if (var1 != null) { stack.push(var1); } @@ -429,10 +416,10 @@ public class InstructionImpact { break; case CodeConstants.opc_aaload: var1 = stack.pop(2); - stack.push(new VarType(var1.type, var1.arraydim - 1, var1.value)); + stack.push(new VarType(var1.type, var1.arrayDim - 1, var1.value)); break; case CodeConstants.opc_astore: - data.setVariable(instr.getOperand(0), stack.pop()); + data.setVariable(instr.operand(0), stack.pop()); break; case CodeConstants.opc_dup: case CodeConstants.opc_dup_x1: @@ -454,19 +441,19 @@ public class InstructionImpact { case CodeConstants.opc_getfield: stack.pop(); case CodeConstants.opc_getstatic: - ck = pool.getLinkConstant(instr.getOperand(0)); + ck = pool.getLinkConstant(instr.operand(0)); var1 = new VarType(ck.descriptor); stack.push(var1); - if (var1.stack_size == 2) { + if (var1.stackSize == 2) { stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); } break; case CodeConstants.opc_putfield: stack.pop(); case CodeConstants.opc_putstatic: - ck = pool.getLinkConstant(instr.getOperand(0)); + ck = pool.getLinkConstant(instr.operand(0)); var1 = new VarType(ck.descriptor); - stack.pop(var1.stack_size); + stack.pop(var1.stackSize); break; case CodeConstants.opc_invokevirtual: case CodeConstants.opc_invokespecial: @@ -474,29 +461,27 @@ public class InstructionImpact { stack.pop(); case CodeConstants.opc_invokestatic: case CodeConstants.opc_invokedynamic: - if (instr.opcode != CodeConstants.opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) { - ck = pool.getLinkConstant(instr.getOperand(0)); + if (instr.opcode != CodeConstants.opc_invokedynamic || instr.bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) { + ck = pool.getLinkConstant(instr.operand(0)); MethodDescriptor md = MethodDescriptor.parseDescriptor(ck.descriptor); for (int i = 0; i < md.params.length; i++) { - stack.pop(md.params[i].stack_size); + stack.pop(md.params[i].stackSize); } if (md.ret.type != CodeConstants.TYPE_VOID) { stack.push(md.ret); - if (md.ret.stack_size == 2) { + if (md.ret.stackSize == 2) { stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY)); } } } break; case CodeConstants.opc_new: - cn = pool.getPrimitiveConstant(instr.getOperand(0)); + cn = pool.getPrimitiveConstant(instr.operand(0)); stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString())); break; case CodeConstants.opc_newarray: stack.pop(); - var1 = new VarType(arr_type[instr.getOperand(0) - 4]); - var1.arraydim = 1; - stack.push(var1); + stack.push(new VarType(arr_type[instr.operand(0) - 4], 1).resizeArrayDim(1)); break; case CodeConstants.opc_athrow: var1 = stack.pop(); @@ -506,17 +491,17 @@ public class InstructionImpact { case CodeConstants.opc_checkcast: case CodeConstants.opc_instanceof: stack.pop(); - cn = pool.getPrimitiveConstant(instr.getOperand(0)); + cn = pool.getPrimitiveConstant(instr.operand(0)); stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString())); break; case CodeConstants.opc_anewarray: case CodeConstants.opc_multianewarray: - int dimensions = (instr.opcode == CodeConstants.opc_anewarray) ? 1 : instr.getOperand(1); + int dimensions = (instr.opcode == CodeConstants.opc_anewarray) ? 1 : instr.operand(1); stack.pop(dimensions); - cn = pool.getPrimitiveConstant(instr.getOperand(0)); + cn = pool.getPrimitiveConstant(instr.operand(0)); if (cn.isArray) { var1 = new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString()); - var1.arraydim += dimensions; + var1 = var1.resizeArrayDim(var1.arrayDim + dimensions); stack.push(var1); } else { diff --git a/src/org/jetbrains/java/decompiler/code/interpreter/Util.java b/src/org/jetbrains/java/decompiler/code/interpreter/Util.java deleted file mode 100644 index bf1e5eb..0000000 --- a/src/org/jetbrains/java/decompiler/code/interpreter/Util.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.interpreter; - -import org.jetbrains.java.decompiler.code.Instruction; -import org.jetbrains.java.decompiler.struct.StructClass; -import org.jetbrains.java.decompiler.struct.StructContext; - - -// FIXME: move to StructContext -public class Util { - - private static final String[][] runtime_exceptions = { - - null, // public final static int opc_nop = 0; - null, // public final static int opc_aconst_null = 1; - null, // public final static int opc_iconst_m1 = 2; - null, // public final static int opc_iconst_0 = 3; - null, // public final static int opc_iconst_1 = 4; - null, // public final static int opc_iconst_2 = 5; - null, // public final static int opc_iconst_3 = 6; - null, // public final static int opc_iconst_4 = 7; - null, // public final static int opc_iconst_5 = 8; - null, // public final static int opc_lconst_0 = 9; - null, // public final static int opc_lconst_1 = 10; - null, // public final static int opc_fconst_0 = 11; - null, // public final static int opc_fconst_1 = 12; - null, // public final static int opc_fconst_2 = 13; - null, // public final static int opc_dconst_0 = 14; - null, // public final static int opc_dconst_1 = 15; - null, // public final static int opc_bipush = 16; - null, // public final static int opc_sipush = 17; - null, // public final static int opc_ldc = 18; - null, // public final static int opc_ldc_w = 19; - null, // public final static int opc_ldc2_w = 20; - null, // public final static int opc_iload = 21; - null, // public final static int opc_lload = 22; - null, // public final static int opc_fload = 23; - null, // public final static int opc_dload = 24; - null, // public final static int opc_aload = 25; - null, // public final static int opc_iload_0 = 26; - null, // public final static int opc_iload_1 = 27; - null, // public final static int opc_iload_2 = 28; - null, // public final static int opc_iload_3 = 29; - null, // public final static int opc_lload_0 = 30; - null, // public final static int opc_lload_1 = 31; - null, // public final static int opc_lload_2 = 32; - null, // public final static int opc_lload_3 = 33; - null, // public final static int opc_fload_0 = 34; - null, // public final static int opc_fload_1 = 35; - null, // public final static int opc_fload_2 = 36; - null, // public final static int opc_fload_3 = 37; - null, // public final static int opc_dload_0 = 38; - null, // public final static int opc_dload_1 = 39; - null, // public final static int opc_dload_2 = 40; - null, // public final static int opc_dload_3 = 41; - null, // public final static int opc_aload_0 = 42; - null, // public final static int opc_aload_1 = 43; - null, // public final static int opc_aload_2 = 44; - null, // public final static int opc_aload_3 = 45; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_iaload = 46; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_laload = 47; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_faload = 48; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_daload = 49; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_aaload = 50; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_baload = 51; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_caload = 52; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_saload = 53; - null, // public final static int opc_istore = 54; - null, // public final static int opc_lstore = 55; - null, // public final static int opc_fstore = 56; - null, // public final static int opc_dstore = 57; - null, // public final static int opc_astore = 58; - null, // public final static int opc_istore_0 = 59; - null, // public final static int opc_istore_1 = 60; - null, // public final static int opc_istore_2 = 61; - null, // public final static int opc_istore_3 = 62; - null, // public final static int opc_lstore_0 = 63; - null, // public final static int opc_lstore_1 = 64; - null, // public final static int opc_lstore_2 = 65; - null, // public final static int opc_lstore_3 = 66; - null, // public final static int opc_fstore_0 = 67; - null, // public final static int opc_fstore_1 = 68; - null, // public final static int opc_fstore_2 = 69; - null, // public final static int opc_fstore_3 = 70; - null, // public final static int opc_dstore_0 = 71; - null, // public final static int opc_dstore_1 = 72; - null, // public final static int opc_dstore_2 = 73; - null, // public final static int opc_dstore_3 = 74; - null, // public final static int opc_astore_0 = 75; - null, // public final static int opc_astore_1 = 76; - null, // public final static int opc_astore_2 = 77; - null, // public final static int opc_astore_3 = 78; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_iastore = 79; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_lastore = 80; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_fastore = 81; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_dastore = 82; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException", "java/lang/ArrayStoreException"}, - // public final static int opc_aastore = 83; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_bastore = 84; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_castore = 85; - {"java/lang/NullPointerException", "java/lang/ArrayIndexOutOfBoundsException"}, - // public final static int opc_sastore = 86; - null, // public final static int opc_pop = 87; - null, // public final static int opc_pop2 = 88; - null, // public final static int opc_dup = 89; - null, // public final static int opc_dup_x1 = 90; - null, // public final static int opc_dup_x2 = 91; - null, // public final static int opc_dup2 = 92; - null, // public final static int opc_dup2_x1 = 93; - null, // public final static int opc_dup2_x2 = 94; - null, // public final static int opc_swap = 95; - null, // public final static int opc_iadd = 96; - null, // public final static int opc_ladd = 97; - null, // public final static int opc_fadd = 98; - null, // public final static int opc_dadd = 99; - null, // public final static int opc_isub = 100; - null, // public final static int opc_lsub = 101; - null, // public final static int opc_fsub = 102; - null, // public final static int opc_dsub = 103; - null, // public final static int opc_imul = 104; - null, // public final static int opc_lmul = 105; - null, // public final static int opc_fmul = 106; - null, // public final static int opc_dmul = 107; - {"java/lang/ArithmeticException"}, // public final static int opc_idiv = 108; - {"java/lang/ArithmeticException"}, // public final static int opc_ldiv = 109; - null, // public final static int opc_fdiv = 110; - null, // public final static int opc_ddiv = 111; - {"java/lang/ArithmeticException"}, // public final static int opc_irem = 112; - {"java/lang/ArithmeticException"}, // public final static int opc_lrem = 113; - null, // public final static int opc_frem = 114; - null, // public final static int opc_drem = 115; - null, // public final static int opc_ineg = 116; - null, // public final static int opc_lneg = 117; - null, // public final static int opc_fneg = 118; - null, // public final static int opc_dneg = 119; - null, // public final static int opc_ishl = 120; - null, // public final static int opc_lshl = 121; - null, // public final static int opc_ishr = 122; - null, // public final static int opc_lshr = 123; - null, // public final static int opc_iushr = 124; - null, // public final static int opc_lushr = 125; - null, // public final static int opc_iand = 126; - null, // public final static int opc_land = 127; - null, // public final static int opc_ior = 128; - null, // public final static int opc_lor = 129; - null, // public final static int opc_ixor = 130; - null, // public final static int opc_lxor = 131; - null, // public final static int opc_iinc = 132; - null, // public final static int opc_i2l = 133; - null, // public final static int opc_i2f = 134; - null, // public final static int opc_i2d = 135; - null, // public final static int opc_l2i = 136; - null, // public final static int opc_l2f = 137; - null, // public final static int opc_l2d = 138; - null, // public final static int opc_f2i = 139; - null, // public final static int opc_f2l = 140; - null, // public final static int opc_f2d = 141; - null, // public final static int opc_d2i = 142; - null, // public final static int opc_d2l = 143; - null, // public final static int opc_d2f = 144; - null, // public final static int opc_i2b = 145; - null, // public final static int opc_i2c = 146; - null, // public final static int opc_i2s = 147; - null, // public final static int opc_lcmp = 148; - null, // public final static int opc_fcmpl = 149; - null, // public final static int opc_fcmpg = 150; - null, // public final static int opc_dcmpl = 151; - null, // public final static int opc_dcmpg = 152; - null, // public final static int opc_ifeq = 153; - null, // public final static int opc_ifne = 154; - null, // public final static int opc_iflt = 155; - null, // public final static int opc_ifge = 156; - null, // public final static int opc_ifgt = 157; - null, // public final static int opc_ifle = 158; - null, // public final static int opc_if_icmpeq = 159; - null, // public final static int opc_if_icmpne = 160; - null, // public final static int opc_if_icmplt = 161; - null, // public final static int opc_if_icmpge = 162; - null, // public final static int opc_if_icmpgt = 163; - null, // public final static int opc_if_icmple = 164; - null, // public final static int opc_if_acmpeq = 165; - null, // public final static int opc_if_acmpne = 166; - null, // public final static int opc_goto = 167; - null, // public final static int opc_jsr = 168; - null, // public final static int opc_ret = 169; - null, // public final static int opc_tableswitch = 170; - null, // public final static int opc_lookupswitch = 171; - {"java/lang/IllegalMonitorStateException"}, // public final static int opc_ireturn = 172; - {"java/lang/IllegalMonitorStateException"}, // public final static int opc_lreturn = 173; - {"java/lang/IllegalMonitorStateException"}, // public final static int opc_freturn = 174; - {"java/lang/IllegalMonitorStateException"}, // public final static int opc_dreturn = 175; - {"java/lang/IllegalMonitorStateException"}, // public final static int opc_areturn = 176; - {"java/lang/IllegalMonitorStateException"}, // public final static int opc_return = 177; - null, // public final static int opc_getstatic = 178; - null, // public final static int opc_putstatic = 179; - {"java/lang/NullPointerException"}, // public final static int opc_getfield = 180; - {"java/lang/NullPointerException"}, // public final static int opc_putfield = 181; - {"java/lang/NullPointerException", "java/lang/AbstractMethodError", "java/lang/UnsatisfiedLinkError"}, - // public final static int opc_invokevirtual = 182; - {"java/lang/NullPointerException", "java/lang/UnsatisfiedLinkError"}, - // public final static int opc_invokespecial = 183; - {"java/lang/UnsatisfiedLinkError"}, // public final static int opc_invokestatic = 184; - {"java/lang/NullPointerException", "java/lang/IncompatibleClassChangeError", "java/lang/IllegalAccessError", - "java/lang/java/lang/AbstractMethodError", "java/lang/UnsatisfiedLinkError"}, - // public final static int opc_invokeinterface = 185; - null, // public final static int opc_xxxunusedxxx = 186; - null, // public final static int opc_new = 187; - {"java/lang/NegativeArraySizeException"}, // public final static int opc_newarray = 188; - {"java/lang/NegativeArraySizeException"}, // public final static int opc_anewarray = 189; - {"java/lang/NullPointerException"}, // public final static int opc_arraylength = 190; - {"java/lang/NullPointerException", "java/lang/IllegalMonitorStateException"}, - // public final static int opc_athrow = 191; - {"java/lang/ClassCastException"}, // public final static int opc_checkcast = 192; - null, // public final static int opc_instanceof = 193; - {"java/lang/NullPointerException"}, // public final static int opc_monitorenter = 194; - {"java/lang/NullPointerException", "java/lang/IllegalMonitorStateException"}, - // public final static int opc_monitorexit = 195; - null, // public final static int opc_wide = 196; - {"java/lang/NegativeArraySizeException"}, // public final static int opc_multianewarray = 197; - null, // public final static int opc_ifnull = 198; - null, // public final static int opc_ifnonnull = 199; - null, // public final static int opc_goto_w = 200; - null, // public final static int opc_jsr_w = 201; - }; - - - public static boolean instanceOf(StructContext context, String valclass, String refclass) { - - if (valclass.equals(refclass)) { - return true; - } - - StructClass cl = context.getClass(valclass); - if (cl == null) { - return false; - } - - if (cl.superClass != null && instanceOf(context, cl.superClass.getString(), refclass)) { - return true; - } - - int[] interfaces = cl.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - String intfc = cl.getPool().getPrimitiveConstant(interfaces[i]).getString(); - - if (instanceOf(context, intfc, refclass)) { - return true; - } - } - - return false; - } - - - public static String[] getRuntimeExceptions(Instruction instr) { - return runtime_exceptions[instr.opcode]; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/ALOAD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/ALOAD.java deleted file mode 100644 index 788795e..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/ALOAD.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class ALOAD extends Instruction { - - private static int[] opcodes = new int[]{opc_aload_0, opc_aload_1, opc_aload_2, opc_aload_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if (index > 3) { - if (wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_aload); - if (wide) { - out.writeShort(index); - } - else { - out.writeByte(index); - } - } - else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if (index > 3) { - if (wide) { - return 4; - } - else { - return 2; - } - } - else { - return 1; - } - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/ANEWARRAY.java b/src/org/jetbrains/java/decompiler/code/optinstructions/ANEWARRAY.java deleted file mode 100644 index d514ffe..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/ANEWARRAY.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class ANEWARRAY extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_anewarray); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/ASTORE.java b/src/org/jetbrains/java/decompiler/code/optinstructions/ASTORE.java deleted file mode 100644 index 40336f4..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/ASTORE.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class ASTORE extends Instruction { - - private static int[] opcodes = new int[]{opc_astore_0, opc_astore_1, opc_astore_2, opc_astore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if (index > 3) { - if (wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_astore); - if (wide) { - out.writeShort(index); - } - else { - out.writeByte(index); - } - } - else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if (index > 3) { - if (wide) { - return 4; - } - else { - return 2; - } - } - else { - return 1; - } - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/BIPUSH.java b/src/org/jetbrains/java/decompiler/code/optinstructions/BIPUSH.java deleted file mode 100644 index 4686051..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/BIPUSH.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class BIPUSH extends Instruction { - - private static int[] opcodes = - new int[]{opc_iconst_m1, opc_iconst_0, opc_iconst_1, opc_iconst_2, opc_iconst_3, opc_iconst_4, opc_iconst_5}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int value = getOperand(0); - if (value < -1 || value > 5) { - out.writeByte(opc_bipush); - out.writeByte(value); - } - else { - out.writeByte(opcodes[value + 1]); - } - } - - public int length() { - int value = getOperand(0); - if (value < -1 || value > 5) { - return 2; - } - else { - return 1; - } - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/CHECKCAST.java b/src/org/jetbrains/java/decompiler/code/optinstructions/CHECKCAST.java deleted file mode 100644 index 9a42f65..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/CHECKCAST.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class CHECKCAST extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_checkcast); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} - diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/DLOAD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/DLOAD.java deleted file mode 100644 index 7630185..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/DLOAD.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class DLOAD extends Instruction { - - private static int[] opcodes = new int[]{opc_dload_0, opc_dload_1, opc_dload_2, opc_dload_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if (index > 3) { - if (wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_dload); - if (wide) { - out.writeShort(index); - } - else { - out.writeByte(index); - } - } - else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if (index > 3) { - if (wide) { - return 4; - } - else { - return 2; - } - } - else { - return 1; - } - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/DSTORE.java b/src/org/jetbrains/java/decompiler/code/optinstructions/DSTORE.java deleted file mode 100644 index 2bdb749..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/DSTORE.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class DSTORE extends Instruction { - - private static int[] opcodes = new int[]{opc_dstore_0, opc_dstore_1, opc_dstore_2, opc_dstore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if (index > 3) { - if (wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_dstore); - if (wide) { - out.writeShort(index); - } - else { - out.writeByte(index); - } - } - else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if (index > 3) { - if (wide) { - return 4; - } - else { - return 2; - } - } - else { - return 1; - } - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/FLOAD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/FLOAD.java deleted file mode 100644 index 8a89dff..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/FLOAD.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class FLOAD extends Instruction { - - private static int[] opcodes = new int[]{opc_fload_0, opc_fload_1, opc_fload_2, opc_fload_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if (index > 3) { - if (wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_fload); - if (wide) { - out.writeShort(index); - } - else { - out.writeByte(index); - } - } - else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if (index > 3) { - if (wide) { - return 4; - } - else { - return 2; - } - } - else { - return 1; - } - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/FSTORE.java b/src/org/jetbrains/java/decompiler/code/optinstructions/FSTORE.java deleted file mode 100644 index 9061913..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/FSTORE.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class FSTORE extends Instruction { - - private static int[] opcodes = new int[]{opc_fstore_0, opc_fstore_1, opc_fstore_2, opc_fstore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if (index > 3) { - if (wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_fstore); - if (wide) { - out.writeShort(index); - } - else { - out.writeByte(index); - } - } - else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if (index > 3) { - if (wide) { - return 4; - } - else { - return 2; - } - } - else { - return 1; - } - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/GETFIELD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/GETFIELD.java deleted file mode 100644 index 6b95aaf..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/GETFIELD.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class GETFIELD extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_getfield); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/GETSTATIC.java b/src/org/jetbrains/java/decompiler/code/optinstructions/GETSTATIC.java deleted file mode 100644 index c1401a6..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/GETSTATIC.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class GETSTATIC extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_getstatic); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO.java b/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO.java deleted file mode 100644 index 97374c0..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.JumpInstruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class GOTO extends JumpInstruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int operand = getOperand(0); - if (operand < -32768 || operand > 32767) { - out.writeByte(opc_goto_w); - out.writeInt(operand); - } - else { - out.writeByte(opc_goto); - out.writeShort(operand); - } - } - - public int length() { - int operand = getOperand(0); - if (operand < -32768 || operand > 32767) { - return 5; - } - else { - return 3; - } - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO_W.java b/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO_W.java deleted file mode 100644 index 56ba9e8..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO_W.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.JumpInstruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class GOTO_W extends JumpInstruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_goto_w); - out.writeInt(getOperand(0)); - } - - public int length() { - return 5; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/IINC.java b/src/org/jetbrains/java/decompiler/code/optinstructions/IINC.java deleted file mode 100644 index 05b446c..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/IINC.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class IINC extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - if (wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_iinc); - if (wide) { - out.writeShort(getOperand(0)); - out.writeShort(getOperand(1)); - } - else { - out.writeByte(getOperand(0)); - out.writeByte(getOperand(1)); - } - } - - public int length() { - return wide ? 6 : 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/ILOAD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/ILOAD.java deleted file mode 100644 index 24b95ff..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/ILOAD.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class ILOAD extends Instruction { - - private static int[] opcodes = new int[]{opc_iload_0, opc_iload_1, opc_iload_2, opc_iload_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if (index > 3) { - if (wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_iload); - if (wide) { - out.writeShort(index); - } - else { - out.writeByte(index); - } - } - else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if (index > 3) { - return wide ? 4 : 2; - } - else { - return 1; - } - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/INSTANCEOF.java b/src/org/jetbrains/java/decompiler/code/optinstructions/INSTANCEOF.java deleted file mode 100644 index f93d5b8..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/INSTANCEOF.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class INSTANCEOF extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_instanceof); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEDYNAMIC.java b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEDYNAMIC.java deleted file mode 100644 index 917c5bd..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEDYNAMIC.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class INVOKEDYNAMIC extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokedynamic); - out.writeShort(getOperand(0)); - out.writeByte(0); - out.writeByte(0); - } - - public int length() { - return 5; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEINTERFACE.java b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEINTERFACE.java deleted file mode 100644 index 51259ff..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEINTERFACE.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class INVOKEINTERFACE extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokeinterface); - out.writeShort(getOperand(0)); - out.writeByte(getOperand(1)); - out.writeByte(0); - } - - public int length() { - return 5; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESPECIAL.java b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESPECIAL.java deleted file mode 100644 index 2436803..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESPECIAL.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class INVOKESPECIAL extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokespecial); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESTATIC.java b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESTATIC.java deleted file mode 100644 index f036a1d..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESTATIC.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class INVOKESTATIC extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokestatic); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEVIRTUAL.java b/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEVIRTUAL.java deleted file mode 100644 index ca3bcd3..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEVIRTUAL.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class INVOKEVIRTUAL extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_invokevirtual); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/ISTORE.java b/src/org/jetbrains/java/decompiler/code/optinstructions/ISTORE.java deleted file mode 100644 index 5907538..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/ISTORE.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class ISTORE extends Instruction { - - private static int[] opcodes = new int[]{opc_istore_0, opc_istore_1, opc_istore_2, opc_istore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if (index > 3) { - if (wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_istore); - if (wide) { - out.writeShort(index); - } - else { - out.writeByte(index); - } - } - else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if (index > 3) { - return wide ? 4 : 2; - } - else { - return 1; - } - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/JSR.java b/src/org/jetbrains/java/decompiler/code/optinstructions/JSR.java deleted file mode 100644 index bb000a7..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/JSR.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.JumpInstruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class JSR extends JumpInstruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int operand = getOperand(0); - if (operand < -32768 || operand > 32767) { - out.writeByte(opc_jsr_w); - out.writeInt(operand); - } - else { - out.writeByte(opc_jsr); - out.writeShort(operand); - } - } - - public int length() { - int operand = getOperand(0); - if (operand < -32768 || operand > 32767) { - return 5; - } - else { - return 3; - } - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/JSR_W.java b/src/org/jetbrains/java/decompiler/code/optinstructions/JSR_W.java deleted file mode 100644 index fb04ed9..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/JSR_W.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.JumpInstruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class JSR_W extends JumpInstruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_jsr_w); - out.writeInt(getOperand(0)); - } - - public int length() { - return 5; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/LDC.java b/src/org/jetbrains/java/decompiler/code/optinstructions/LDC.java deleted file mode 100644 index ae7b3c6..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/LDC.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class LDC extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ldc); - out.writeByte(getOperand(0)); - } - - public int length() { - return 2; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/LDC2_W.java b/src/org/jetbrains/java/decompiler/code/optinstructions/LDC2_W.java deleted file mode 100644 index 6ba1964..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/LDC2_W.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class LDC2_W extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ldc2_w); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/LDC_W.java b/src/org/jetbrains/java/decompiler/code/optinstructions/LDC_W.java deleted file mode 100644 index 71e5fe0..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/LDC_W.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class LDC_W extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_ldc_w); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/LLOAD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/LLOAD.java deleted file mode 100644 index 6e50659..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/LLOAD.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class LLOAD extends Instruction { - - private static int[] opcodes = new int[]{opc_lload_0, opc_lload_1, opc_lload_2, opc_lload_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if (index > 3) { - if (wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_lload); - if (wide) { - out.writeShort(index); - } - else { - out.writeByte(index); - } - } - else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if (index > 3) { - return wide ? 4 : 2; - } - else { - return 1; - } - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/LOOKUPSWITCH.java b/src/org/jetbrains/java/decompiler/code/optinstructions/LOOKUPSWITCH.java deleted file mode 100644 index 4183168..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/LOOKUPSWITCH.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.SwitchInstruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class LOOKUPSWITCH extends SwitchInstruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - - out.writeByte(opc_lookupswitch); - - int padding = 3 - (offset % 4); - for (int i = 0; i < padding; i++) { - out.writeByte(0); - } - - for (int i = 0; i < operandsCount(); i++) { - out.writeInt(getOperand(i)); - } - } - - public int length() { - return 1 + operandsCount() * 4; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/LSTORE.java b/src/org/jetbrains/java/decompiler/code/optinstructions/LSTORE.java deleted file mode 100644 index 9095f04..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/LSTORE.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class LSTORE extends Instruction { - - private static int[] opcodes = new int[]{opc_lstore_0, opc_lstore_1, opc_lstore_2, opc_lstore_3}; - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - int index = getOperand(0); - if (index > 3) { - if (wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_lstore); - if (wide) { - out.writeShort(index); - } - else { - out.writeByte(index); - } - } - else { - out.writeByte(opcodes[index]); - } - } - - public int length() { - int index = getOperand(0); - if (index > 3) { - return wide ? 4 : 2; - } - else { - return 1; - } - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/MULTIANEWARRAY.java b/src/org/jetbrains/java/decompiler/code/optinstructions/MULTIANEWARRAY.java deleted file mode 100644 index b6835e8..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/MULTIANEWARRAY.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class MULTIANEWARRAY extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_multianewarray); - out.writeShort(getOperand(0)); - out.writeByte(getOperand(1)); - } - - public int length() { - return 4; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/NEW.java b/src/org/jetbrains/java/decompiler/code/optinstructions/NEW.java deleted file mode 100644 index 2e8854f..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/NEW.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class NEW extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_new); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/NEWARRAY.java b/src/org/jetbrains/java/decompiler/code/optinstructions/NEWARRAY.java deleted file mode 100644 index f2f8e4d..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/NEWARRAY.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class NEWARRAY extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_newarray); - out.writeByte(getOperand(0)); - } - - public int length() { - return 2; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/PUTFIELD.java b/src/org/jetbrains/java/decompiler/code/optinstructions/PUTFIELD.java deleted file mode 100644 index c1e3e92..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/PUTFIELD.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class PUTFIELD extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_putfield); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/PUTSTATIC.java b/src/org/jetbrains/java/decompiler/code/optinstructions/PUTSTATIC.java deleted file mode 100644 index 0fe0240..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/PUTSTATIC.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class PUTSTATIC extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_putstatic); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/RET.java b/src/org/jetbrains/java/decompiler/code/optinstructions/RET.java deleted file mode 100644 index 93f62ff..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/RET.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class RET extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - if (wide) { - out.writeByte(opc_wide); - } - out.writeByte(opc_ret); - if (wide) { - out.writeShort(getOperand(0)); - } - else { - out.writeByte(getOperand(0)); - } - } - - public int length() { - return wide ? 4 : 2; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/SIPUSH.java b/src/org/jetbrains/java/decompiler/code/optinstructions/SIPUSH.java deleted file mode 100644 index 08b10a5..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/SIPUSH.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.Instruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class SIPUSH extends Instruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - out.writeByte(opc_sipush); - out.writeShort(getOperand(0)); - } - - public int length() { - return 3; - } -} diff --git a/src/org/jetbrains/java/decompiler/code/optinstructions/TABLESWITCH.java b/src/org/jetbrains/java/decompiler/code/optinstructions/TABLESWITCH.java deleted file mode 100644 index 0b41ecf..0000000 --- a/src/org/jetbrains/java/decompiler/code/optinstructions/TABLESWITCH.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.code.optinstructions; - -import org.jetbrains.java.decompiler.code.SwitchInstruction; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class TABLESWITCH extends SwitchInstruction { - - public void writeToStream(DataOutputStream out, int offset) throws IOException { - - out.writeByte(opc_tableswitch); - - int padding = 3 - (offset % 4); - for (int i = 0; i < padding; i++) { - out.writeByte(0); - } - - for (int i = 0; i < operandsCount(); i++) { - out.writeInt(getOperand(i)); - } - } - - public int length() { - return 1 + operandsCount() * 4; - } -} diff --git a/src/org/jetbrains/java/decompiler/main/AssertProcessor.java b/src/org/jetbrains/java/decompiler/main/AssertProcessor.java index a58b689..a8863c5 100644 --- a/src/org/jetbrains/java/decompiler/main/AssertProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/AssertProcessor.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -35,13 +21,13 @@ 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"); public static void buildAssertions(ClassNode node) { - ClassWrapper wrapper = node.wrapper; + ClassWrapper wrapper = node.getWrapper(); StructField field = findAssertionField(node); @@ -67,7 +53,7 @@ public class AssertProcessor { private static StructField findAssertionField(ClassNode node) { - ClassWrapper wrapper = node.wrapper; + ClassWrapper wrapper = node.getWrapper(); boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); @@ -89,7 +75,7 @@ public class AssertProcessor { if (initializer.type == Exprent.EXPRENT_FUNCTION) { FunctionExprent fexpr = (FunctionExprent)initializer; - if (fexpr.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT && + if (fexpr.getFuncType() == FunctionExprent.FUNCTION_BOOL_NOT && fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_INVOCATION) { InvocationExprent invexpr = (InvocationExprent)fexpr.getLstOperands().get(0); @@ -101,11 +87,11 @@ public class AssertProcessor { invexpr.getLstParameters().isEmpty()) { ConstExprent cexpr = (ConstExprent)invexpr.getInstance(); - if (VarType.VARTYPE_CLASS.equals(cexpr.getConsttype())) { + if (VarType.VARTYPE_CLASS.equals(cexpr.getConstType())) { ClassNode nd = node; while (nd != null) { - if (nd.wrapper.getClassStruct().qualifiedName.equals(cexpr.getValue())) { + if (nd.getWrapper().getClassStruct().qualifiedName.equals(cexpr.getValue())) { break; } nd = nd.parent; @@ -157,26 +143,42 @@ public class AssertProcessor { private static boolean replaceAssertion(Statement parent, IfStatement stat, String classname, String key) { + boolean throwInIf = true; Statement ifstat = stat.getIfstat(); InvocationExprent throwError = isAssertionError(ifstat); if (throwError == null) { - return false; + //check else: + Statement elsestat = stat.getElsestat(); + throwError = isAssertionError(elsestat); + if (throwError == null) { + return false; + } + else { + throwInIf = false; + } } - Object[] exprres = getAssertionExprent(stat.getHeadexprent().getCondition().copy(), classname, key); + + Object[] exprres = getAssertionExprent(stat.getHeadexprent().getCondition().copy(), classname, key, throwInIf); if (!(Boolean)exprres[1]) { return false; } - List lstParams = new ArrayList(); + List lstParams = new ArrayList<>(); Exprent ascond = null, retcond = null; - if (exprres[0] != null) { - ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, - Arrays.asList(new Exprent[]{(Exprent)exprres[0]})); - retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond); + if (throwInIf) { + if (exprres[0] != null) { + ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, (Exprent)exprres[0], throwError.bytecode); + retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond); + } } + else { + ascond = (Exprent) exprres[0]; + retcond = ascond; + } + lstParams.add(retcond == null ? ascond : retcond); if (!throwError.getLstParameters().isEmpty()) { @@ -197,13 +199,18 @@ public class AssertProcessor { first.removeSuccessor(stat.getIfEdge()); first.removeSuccessor(stat.getElseEdge()); - List lstStatements = new ArrayList(); + List lstStatements = new ArrayList<>(); if (first.getExprents() != null && !first.getExprents().isEmpty()) { lstStatements.add(first); } lstStatements.add(newstat); if (stat.iftype == IfStatement.IFTYPE_IFELSE) { - lstStatements.add(stat.getElsestat()); + if (throwInIf) { + lstStatements.add(stat.getElsestat()); + } + else { + lstStatements.add(stat.getIfstat()); + } } SequenceStatement sequence = new SequenceStatement(lstStatements); @@ -214,10 +221,16 @@ public class AssertProcessor { sequence.getStats().get(i), sequence.getStats().get(i + 1))); } - if (stat.iftype == IfStatement.IFTYPE_IFELSE) { - Statement ifelse = stat.getElsestat(); + if (stat.iftype == IfStatement.IFTYPE_IFELSE || !throwInIf) { + Statement stmts; + if (throwInIf) { + stmts = stat.getElsestat(); + } + else { + stmts = stat.getIfstat(); + } - List lstSuccs = ifelse.getAllSuccessorEdges(); + List lstSuccs = stmts.getAllSuccessorEdges(); if (!lstSuccs.isEmpty()) { StatEdge endedge = lstSuccs.get(0); if (endedge.closure == stat) { @@ -245,9 +258,9 @@ public class AssertProcessor { if (expr.type == Exprent.EXPRENT_EXIT) { ExitExprent exexpr = (ExitExprent)expr; - if (exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_NEW) { + if (exexpr.getExitType() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_NEW) { NewExprent nexpr = (NewExprent)exexpr.getValue(); - if (CLASS_ASSERTION_ERROR.equals(nexpr.getNewtype()) && nexpr.getConstructor() != null) { + if (CLASS_ASSERTION_ERROR.equals(nexpr.getNewType()) && nexpr.getConstructor() != null) { return nexpr.getConstructor(); } } @@ -256,16 +269,21 @@ public class AssertProcessor { return null; } - private static Object[] getAssertionExprent(Exprent exprent, String classname, String key) { + private static Object[] getAssertionExprent(Exprent exprent, String classname, String key, boolean throwInIf) { if (exprent.type == Exprent.EXPRENT_FUNCTION) { + int desiredOperation = FunctionExprent.FUNCTION_CADD; + if (!throwInIf) { + desiredOperation = FunctionExprent.FUNCTION_COR; + } + FunctionExprent fexpr = (FunctionExprent)exprent; - if (fexpr.getFunctype() == FunctionExprent.FUNCTION_CADD) { + if (fexpr.getFuncType() == desiredOperation) { for (int i = 0; i < 2; i++) { Exprent param = fexpr.getLstOperands().get(i); - if (isAssertionField(param, classname, key)) { + if (isAssertionField(param, classname, key, throwInIf)) { return new Object[]{fexpr.getLstOperands().get(1 - i), true}; } } @@ -273,7 +291,7 @@ public class AssertProcessor { for (int i = 0; i < 2; i++) { Exprent param = fexpr.getLstOperands().get(i); - Object[] res = getAssertionExprent(param, classname, key); + Object[] res = getAssertionExprent(param, classname, key, throwInIf); if ((Boolean)res[1]) { if (param != res[0]) { fexpr.getLstOperands().set(i, (Exprent)res[0]); @@ -282,7 +300,7 @@ public class AssertProcessor { } } } - else if (isAssertionField(fexpr, classname, key)) { + else if (isAssertionField(fexpr, classname, key, throwInIf)) { // assert false; return new Object[]{null, true}; } @@ -291,20 +309,25 @@ public class AssertProcessor { return new Object[]{exprent, false}; } - private static boolean isAssertionField(Exprent exprent, String classname, String key) { - - if (exprent.type == Exprent.EXPRENT_FUNCTION) { - FunctionExprent fparam = (FunctionExprent)exprent; - if (fparam.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT && - fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { - FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0); - if (classname.equals(fdparam.getClassname()) - && key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString))) { - return true; + private static boolean isAssertionField(Exprent exprent, String classname, String key, boolean throwInIf) { + if (throwInIf) { + if (exprent.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fparam = (FunctionExprent)exprent; + if (fparam.getFuncType() == FunctionExprent.FUNCTION_BOOL_NOT && + fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { + FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0); + return classname.equals(fdparam.getClassname()) && + key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString)); } } } - + else { + if (exprent.type == Exprent.EXPRENT_FIELD) { + FieldExprent fdparam = (FieldExprent) exprent; + return classname.equals(fdparam.getClassname()) && + key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString)); + } + } return false; } } diff --git a/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java b/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java index 3807d2e..e082267 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -36,79 +22,48 @@ 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; - public ExitExprent bodyexprent; + static { + InvocationExprent invFor = new InvocationExprent(); + invFor.setName("forName"); + invFor.setClassname("java/lang/Class"); + invFor.setStringDescriptor("(Ljava/lang/String;)Ljava/lang/Class;"); + invFor.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/String;)Ljava/lang/Class;")); + invFor.setStatic(true); + invFor.setLstParameters(Collections.singletonList(new VarExprent(0, VarType.VARTYPE_STRING, null))); + BODY_EXPR = new ExitExprent(ExitExprent.EXIT_RETURN, invFor, VarType.VARTYPE_CLASS, null); - public ExitExprent handlerexprent; - - - public ClassReference14Processor() { - - InvocationExprent invfor = new InvocationExprent(); - invfor.setName("forName"); - invfor.setClassname("java/lang/Class"); - invfor.setStringDescriptor("(Ljava/lang/String;)Ljava/lang/Class;"); - invfor.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/String;)Ljava/lang/Class;")); - invfor.setStatic(true); - invfor.setLstParameters(Arrays.asList(new Exprent[]{new VarExprent(0, VarType.VARTYPE_STRING, null)})); - - bodyexprent = new ExitExprent(ExitExprent.EXIT_RETURN, - invfor, - VarType.VARTYPE_CLASS); - - InvocationExprent constr = new InvocationExprent(); - constr.setName(""); - constr.setClassname("java/lang/NoClassDefFoundError"); - constr.setStringDescriptor("()V"); - constr.setFunctype(InvocationExprent.TYP_INIT); - constr.setDescriptor(MethodDescriptor.parseDescriptor("()V")); - - NewExprent newexpr = - new NewExprent(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/NoClassDefFoundError"), new ArrayList()); - newexpr.setConstructor(constr); - - InvocationExprent invcause = new InvocationExprent(); - invcause.setName("initCause"); - invcause.setClassname("java/lang/NoClassDefFoundError"); - invcause.setStringDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); - invcause.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;")); - invcause.setInstance(newexpr); - invcause.setLstParameters( - Arrays.asList(new Exprent[]{new VarExprent(2, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"), null)})); - - handlerexprent = new ExitExprent(ExitExprent.EXIT_THROW, - invcause, - null); + InvocationExprent ctor = new InvocationExprent(); + ctor.setName(CodeConstants.INIT_NAME); + ctor.setClassname("java/lang/NoClassDefFoundError"); + ctor.setStringDescriptor("()V"); + ctor.setFunctype(InvocationExprent.TYP_INIT); + ctor.setDescriptor(MethodDescriptor.parseDescriptor("()V")); + NewExprent newExpr = new NewExprent(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/NoClassDefFoundError"), new ArrayList<>(), null); + newExpr.setConstructor(ctor); + InvocationExprent invCause = new InvocationExprent(); + invCause.setName("initCause"); + invCause.setClassname("java/lang/NoClassDefFoundError"); + invCause.setStringDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); + invCause.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;")); + invCause.setInstance(newExpr); + invCause.setLstParameters( + Collections.singletonList(new VarExprent(2, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"), null))); + HANDLER_EXPR = new ExitExprent(ExitExprent.EXIT_THROW, invCause, null, null); } - - public void processClassReferences(ClassNode node) { - - ClassWrapper wrapper = node.wrapper; - - // int major_version = wrapper.getClassStruct().major_version; - // int minor_version = wrapper.getClassStruct().minor_version; - // - // if(major_version > 48 || (major_version == 48 && minor_version > 0)) { - // // version 1.5 or above - // return; - // } - - if (wrapper.getClassStruct().isVersionGE_1_5()) { - // version 1.5 or above - return; - } - + public static void processClassReferences(ClassNode node) { // find the synthetic method Class class$(String) if present - HashMap mapClassMeths = new HashMap(); + Map mapClassMeths = new HashMap<>(); mapClassMethods(node, mapClassMeths); - if (mapClassMeths.isEmpty()) { return; } - HashSet setFound = new HashSet(); + Set setFound = new HashSet<>(); processClassRec(node, mapClassMeths, setFound); if (!setFound.isEmpty()) { @@ -119,29 +74,21 @@ public class ClassReference14Processor { } } - private static void processClassRec(ClassNode node, - final HashMap mapClassMeths, - final HashSet setFound) { - - final ClassWrapper wrapper = node.wrapper; + private static void processClassRec(ClassNode node, Map mapClassMeths, Set setFound) { + ClassWrapper wrapper = node.getWrapper(); // search code for (MethodWrapper meth : wrapper.getMethods()) { - RootStatement root = meth.root; if (root != null) { - DirectGraph graph = meth.getOrBuildGraph(); - - graph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - for (Entry ent : mapClassMeths.entrySet()) { - if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) { - setFound.add(ent.getKey()); - } + graph.iterateExprents(exprent -> { + for (Entry ent : mapClassMeths.entrySet()) { + if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) { + setFound.add(ent.getKey()); } - return 0; } + return 0; }); } } @@ -160,7 +107,7 @@ public class ClassReference14Processor { String cl = isClass14Invocation(exprent, ent.getKey(), ent.getValue()); if (cl != null) { - initializers.set(i, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'))); + initializers.set(i, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'), exprent.bytecode)); setFound.add(ent.getKey()); } } @@ -173,10 +120,10 @@ public class ClassReference14Processor { } } - private void mapClassMethods(ClassNode node, Map map) { + private static void mapClassMethods(ClassNode node, Map map) { boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); - ClassWrapper wrapper = node.wrapper; + ClassWrapper wrapper = node.getWrapper(); for (MethodWrapper method : wrapper.getMethods()) { StructMethod mt = method.methodStruct; @@ -190,14 +137,14 @@ public class ClassReference14Processor { CatchStatement cst = (CatchStatement)root.getFirst(); if (cst.getStats().size() == 2 && cst.getFirst().type == Statement.TYPE_BASICBLOCK && cst.getStats().get(1).type == Statement.TYPE_BASICBLOCK && - cst.getVars().get(0).getVartype().equals(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"))) { + cst.getVars().get(0).getVarType().equals(new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"))) { BasicBlockStatement body = (BasicBlockStatement)cst.getFirst(); BasicBlockStatement handler = (BasicBlockStatement)cst.getStats().get(1); if (body.getExprents().size() == 1 && handler.getExprents().size() == 1) { - if (bodyexprent.equals(body.getExprents().get(0)) && - handlerexprent.equals(handler.getExprents().get(0))) { + if (BODY_EXPR.equals(body.getExprents().get(0)) && + HANDLER_EXPR.equals(handler.getExprents().get(0))) { map.put(wrapper, method); break; } @@ -213,19 +160,16 @@ public class ClassReference14Processor { } } - private static boolean replaceInvocations(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) { - boolean res = false; while (true) { - boolean found = false; for (Exprent expr : exprent.getAllExprents()) { String cl = isClass14Invocation(expr, wrapper, meth); if (cl != null) { - exprent.replaceExprent(expr, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'))); + exprent.replaceExprent(expr, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'), expr.bytecode)); found = true; res = true; break; @@ -242,18 +186,16 @@ public class ClassReference14Processor { return res; } - private static String isClass14Invocation(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) { - if (exprent.type == Exprent.EXPRENT_FUNCTION) { FunctionExprent fexpr = (FunctionExprent)exprent; - if (fexpr.getFunctype() == FunctionExprent.FUNCTION_IIF) { + if (fexpr.getFuncType() == FunctionExprent.FUNCTION_IIF) { if (fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) { FunctionExprent headexpr = (FunctionExprent)fexpr.getLstOperands().get(0); - if (headexpr.getFunctype() == FunctionExprent.FUNCTION_EQ) { + if (headexpr.getFuncType() == FunctionExprent.FUNCTION_EQ) { if (headexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD && headexpr.getLstOperands().get(1).type == Exprent.EXPRENT_CONST && - ((ConstExprent)headexpr.getLstOperands().get(1)).getConsttype().equals(VarType.VARTYPE_NULL)) { + ((ConstExprent)headexpr.getLstOperands().get(1)).getConstType().equals(VarType.VARTYPE_NULL)) { FieldExprent field = (FieldExprent)headexpr.getLstOperands().get(0); ClassNode fieldnode = DecompilerContext.getClassProcessor().getMapRootClasses().get(field.getClassname()); @@ -293,4 +235,4 @@ public class ClassReference14Processor { return null; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 6d08a90..72c215b 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -23,18 +9,12 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.rels.ClassWrapper; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +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; @@ -42,30 +22,29 @@ import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.struct.gen.generics.*; import org.jetbrains.java.decompiler.util.InterpreterUtil; -import org.jetbrains.java.decompiler.util.Util; +import org.jetbrains.java.decompiler.util.TextBuffer; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.logging.Logger; +import java.util.stream.Collectors; public class ClassWriter { - - private ClassReference14Processor ref14processor; - private PoolInterceptor interceptor; + private final PoolInterceptor interceptor; public ClassWriter() { - ref14processor = new ClassReference14Processor(); interceptor = DecompilerContext.getPoolInterceptor(); } - private void invokeProcessors(ClassNode node) { - ClassWrapper wrapper = node.wrapper; + private static void invokeProcessors(ClassNode node) { + ClassWrapper wrapper = node.getWrapper(); StructClass cl = wrapper.getClassStruct(); InitializerProcessor.extractInitializers(wrapper); - if (node.type == ClassNode.CLASS_ROOT && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) { - ref14processor.processClassReferences(node); + if (node.type == ClassNode.CLASS_ROOT && + !cl.isVersion5() && + DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) { + ClassReference14Processor.processClassReferences(node); } if (cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM)) { @@ -77,13 +56,9 @@ public class ClassWriter { } } - public void classLambdaToJava(ClassNode node, TextBuffer buffer, Exprent method_object, int indent) { - // get the class node with the content method - ClassNode classNode = node; - while (classNode != null && classNode.type == ClassNode.CLASS_LAMBDA) { - classNode = classNode.parent; - } - if (classNode == null) { + public void classLambdaToJava(ClassNode node, TextBuffer buffer, Exprent method_object, int indent, BytecodeMappingTracer origTracer) { + ClassWrapper wrapper = node.getWrapper(); + if (wrapper == null) { return; } @@ -92,39 +67,38 @@ public class ClassWriter { ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node); - BytecodeMappingTracer tracer = new BytecodeMappingTracer(); + BytecodeMappingTracer tracer = new BytecodeMappingTracer(origTracer.getCurrentSourceLine()); try { - ClassWrapper wrapper = classNode.wrapper; StructClass cl = wrapper.getClassStruct(); DecompilerContext.getLogger().startWriteClass(node.simpleName); - if (node.lambda_information.is_method_reference) { - if (!node.lambda_information.is_content_method_static && method_object != null) { + if (node.lambdaInformation.is_method_reference) { + if (!node.lambdaInformation.is_content_method_static && method_object != null) { // reference to a virtual method buffer.append(method_object.toJava(indent, tracer)); } else { // reference to a static method - buffer.append(ExprProcessor.getCastTypeName(new VarType(node.lambda_information.content_class_name, false))); + buffer.append(ExprProcessor.getCastTypeName(new VarType(node.lambdaInformation.content_class_name, true))); } - buffer.append("::"); - buffer.append("".equals(node.lambda_information.content_method_name) ? "new" : node.lambda_information.content_method_name); + buffer.append("::") + .append(CodeConstants.INIT_NAME.equals(node.lambdaInformation.content_method_name) ? "new" : node.lambdaInformation.content_method_name); } else { // lambda method - StructMethod mt = cl.getMethod(node.lambda_information.content_method_key); + StructMethod mt = cl.getMethod(node.lambdaInformation.content_method_key); MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); - MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node.lambda_information.content_method_descriptor); - MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node.lambda_information.method_descriptor); + MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node.lambdaInformation.content_method_descriptor); + MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node.lambdaInformation.method_descriptor); if (!lambdaToAnonymous) { buffer.append('('); boolean firstParameter = true; - int index = node.lambda_information.is_content_method_static ? 0 : 1; + int index = node.lambdaInformation.is_content_method_static ? 0 : 1; int start_index = md_content.params.length - md_lambda.params.length; for (int i = 0; i < md_content.params.length; i++) { @@ -133,30 +107,26 @@ public class ClassWriter { buffer.append(", "); } - String typeName = ExprProcessor.getCastTypeName(md_content.params[i].copy()); - if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) - && DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { - typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); - } - - String parameterName = methodWrapper.varproc.getVarName(new VarVersionPaar(index, 0, typeName, false)); + String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0)); buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors firstParameter = false; } - index += md_content.params[i].stack_size; + index += md_content.params[i].stackSize; } buffer.append(") ->"); } - buffer.append(" {"); - buffer.append(DecompilerContext.getNewLineSeparator()); + buffer.append(" {").appendLineSeparator(); + tracer.incrementCurrentSourceLine(); - methodLambdaToJava(node, classNode, mt, buffer, indent + 1, !lambdaToAnonymous, tracer); + methodLambdaToJava(node, wrapper, mt, buffer, indent + 1, !lambdaToAnonymous, tracer); buffer.appendIndent(indent).append("}"); + + addTracer(cl, mt, tracer); } } finally { @@ -166,89 +136,72 @@ public class ClassWriter { DecompilerContext.getLogger().endWriteClass(); } - public void classToJava(ClassNode node, TextBuffer buffer, int indent) { + public void classToJava(ClassNode node, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) { ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node); - int total_offset_lines = 0; - BytecodeMappingTracer dummy_tracer = new BytecodeMappingTracer(); + int startLine = tracer != null ? tracer.getCurrentSourceLine() : 0; + BytecodeMappingTracer dummy_tracer = new BytecodeMappingTracer(startLine); try { // last minute processing invokeProcessors(node); - ClassWrapper wrapper = node.wrapper; + ClassWrapper wrapper = node.getWrapper(); StructClass cl = wrapper.getClassStruct(); DecompilerContext.getLogger().startWriteClass(cl.qualifiedName); - String lineSeparator = DecompilerContext.getNewLineSeparator(); - // write class definition int start_class_def = buffer.length(); - boolean empty = cl.getFields().isEmpty() && cl.getMethods().isEmpty(); - if (cl.hasModifier(CodeConstants.ACC_ENUM)) { - empty = true; - for (StructField fd : cl.getFields()) { - if (fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM)) { - empty = false; - break; - } - } - } - writeClassDefinition(node, buffer, indent, empty); - -// // count lines in class definition the easiest way -// total_offset_lines = buffer.substring(start_class_def).toString().split(lineSeparator, -1).length - 1; + writeClassDefinition(node, buffer, indent); boolean hasContent = false; - - // fields boolean enumFields = false; - boolean endEnumWritten = false; // Spigot - boolean firstEnum = true; + + 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) { - buffer.append(", "); + buffer.append(',').appendLineSeparator(); + dummy_tracer.incrementCurrentSourceLine(); } enumFields = true; } - else if (enumFields && !endEnumWritten) { // Spigot + else if (enumFields) { buffer.append(';'); - buffer.append(lineSeparator); - buffer.append(lineSeparator); - endEnumWritten = true; // Spigot + buffer.appendLineSeparator(); + buffer.appendLineSeparator(); + dummy_tracer.incrementCurrentSourceLine(2); + enumFields = false; } - int ind = indent + 1; - if (isEnum) { - if (!firstEnum) { - ind = 0; - } - firstEnum = false; - } - fieldToJava(wrapper, cl, fd, buffer, ind, dummy_tracer); // FIXME: insert real tracer + fieldToJava(wrapper, cl, fd, buffer, indent + 1, dummy_tracer); // FIXME: insert real tracer hasContent = true; } - // Spigot Start - int flags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access; - boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; - if (isEnum && !endEnumWritten) { - // Spigot End - buffer.append(';'); - buffer.append(lineSeparator); + + if (enumFields) { + buffer.append(';').appendLineSeparator(); + dummy_tracer.incrementCurrentSourceLine(); } // FIXME: fields don't matter at the moment - total_offset_lines = buffer.count(lineSeparator, start_class_def); + startLine += buffer.countLines(start_class_def); // methods for (StructMethod mt : cl.getMethods()) { @@ -258,19 +211,21 @@ public class ClassWriter { if (hide) continue; int position = buffer.length(); + int storedLine = startLine; if (hasContent) { - buffer.append(lineSeparator); + buffer.appendLineSeparator(); + startLine++; } - BytecodeMappingTracer method_tracer = new BytecodeMappingTracer(total_offset_lines); + BytecodeMappingTracer method_tracer = new BytecodeMappingTracer(startLine); boolean methodSkipped = !methodToJava(node, mt, buffer, indent + 1, method_tracer); if (!methodSkipped) { hasContent = true; - DecompilerContext.getBytecodeSourceMapper().addTracer(cl.qualifiedName, - InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()), method_tracer); - total_offset_lines = (method_tracer.getCurrentSourceLine() + 1); // zero-based line index + addTracer(cl, mt, method_tracer); + startLine = method_tracer.getCurrentSourceLine(); } else { buffer.setLength(position); + startLine = storedLine; } } @@ -284,9 +239,12 @@ public class ClassWriter { if (hide) continue; if (hasContent) { - buffer.append(lineSeparator); + buffer.appendLineSeparator(); + startLine++; } - classToJava(inner, buffer, indent + 1); + BytecodeMappingTracer class_tracer = new BytecodeMappingTracer(startLine); + classToJava(inner, buffer, indent + 1, class_tracer); + startLine = buffer.countLines(); hasContent = true; } @@ -295,7 +253,7 @@ public class ClassWriter { buffer.appendIndent(indent).append('}'); if (node.type != ClassNode.CLASS_ANONYMOUS) { - buffer.append(lineSeparator); + buffer.appendLineSeparator(); } } finally { @@ -305,39 +263,154 @@ public class ClassWriter { DecompilerContext.getLogger().endWriteClass(); } - private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent, boolean empty) { - String lineSeparator = DecompilerContext.getNewLineSeparator(); + @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.contains("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); + String key = InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor()); + DecompilerContext.getBytecodeSourceMapper().addTracer(cls.qualifiedName, key, tracer); + } + + private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent) { if (node.type == ClassNode.CLASS_ANONYMOUS) { - buffer.append(" {"); - if (!empty) buffer.append(lineSeparator); + buffer.append(" {").appendLineSeparator(); return; } - ClassWrapper wrapper = node.wrapper; + ClassWrapper wrapper = node.getWrapper(); StructClass cl = wrapper.getClassStruct(); int flags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access; - boolean isDeprecated = cl.getAttributes().containsKey("Deprecated"); - boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.getAttributes().containsKey("Synthetic"); + boolean isDeprecated = cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED); + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC); boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0; boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0; if (isDeprecated) { - appendDeprecation(buffer, indent, lineSeparator); + appendDeprecation(buffer, indent); } if (interceptor != null) { String oldName = interceptor.getOldName(cl.qualifiedName); - appendRenameComment(buffer, oldName, MType.CLASS, indent, lineSeparator); + appendRenameComment(buffer, oldName, MType.CLASS, indent); } if (isSynthetic) { - appendComment(buffer, "synthetic class", indent, lineSeparator); + appendComment(buffer, "synthetic class", indent); } - appendAnnotations(buffer, cl, indent, lineSeparator); + appendAnnotations(buffer, indent, cl, -1); buffer.appendIndent(indent); @@ -347,6 +420,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) { @@ -358,87 +438,123 @@ public class ClassWriter { } buffer.append("interface "); } + else if (components != null) { + buffer.append("record "); + } else { buffer.append("class "); } - - GenericClassDescriptor descriptor = null; - if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)cl.getAttributes().getWithKey("Signature"); - if (attr != null) { - descriptor = GenericMain.parseClassSignature(attr.getSignature()); - } - } - buffer.append(node.simpleName); + GenericClassDescriptor descriptor = getGenericClassDescriptor(cl); if (descriptor != null && !descriptor.fparameters.isEmpty()) { appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds); } + 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 "); + String name; if (descriptor != null) { - buffer.append(GenericMain.getGenericCastTypeName(descriptor.superclass)); + name = GenericMain.getGenericCastTypeName(descriptor.superclass); } else { - buffer.append(ExprProcessor.getCastTypeName(supertype)); + name = ExprProcessor.getCastTypeName(supertype); + } + if (!name.equals("Record")) + { + buffer.append("extends "); + buffer.append(name); + buffer.append(' '); } - buffer.append(' '); } } if (!isAnnotation) { int[] interfaces = cl.getInterfaces(); if (interfaces.length > 0) { - buffer.append(isInterface ? "extends " : "implements "); - for (int i = 0; i < interfaces.length; i++) { - if (i > 0) { + + boolean seenOne = false; + for (int i = 0; i < interfaces.length; i++) + { + if (seenOne) + { buffer.append(", "); } - if (descriptor != null) { - buffer.append(GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(i))); + + String name; + if (descriptor != null) + { + name = GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(i)); } - else { - buffer.append(ExprProcessor.getCastTypeName(new VarType(cl.getInterface(i), true))); + else + { + name = ExprProcessor.getCastTypeName(new VarType(cl.getInterface(i), true)); } + + // TODO: evil + if (name.equals("Record")) + { + continue; + } + + if (!seenOne) + { + buffer.append(isInterface ? "extends " : "implements "); + } + + buffer.append(name); + seenOne = true; } buffer.append(' '); } } - buffer.append('{'); - if (!empty) { - buffer.append(lineSeparator); - buffer.append(lineSeparator); // Spigot - } + 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) { - String lineSeparator = DecompilerContext.getNewLineSeparator(); - + int start = buffer.length(); boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE); - boolean isDeprecated = fd.getAttributes().containsKey("Deprecated"); + boolean isDeprecated = fd.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED); boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); if (isDeprecated) { - appendDeprecation(buffer, indent, lineSeparator); + appendDeprecation(buffer, indent); } if (interceptor != null) { String oldName = interceptor.getOldName(cl.qualifiedName + " " + fd.getName() + " " + fd.getDescriptor()); - appendRenameComment(buffer, oldName, MType.FIELD, indent, lineSeparator); + appendRenameComment(buffer, oldName, MType.FIELD, indent); } if (fd.isSynthetic()) { - appendComment(buffer, "synthetic field", indent, lineSeparator); + appendComment(buffer, "synthetic field", indent); } - appendAnnotations(buffer, fd, indent, lineSeparator); + appendAnnotations(buffer, indent, fd, TypeAnnotation.FIELD); buffer.appendIndent(indent); @@ -446,15 +562,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 = (StructGenericSignatureAttribute)fd.getAttributes().getWithKey("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) { @@ -468,6 +578,8 @@ public class ClassWriter { buffer.append(fd.getName()); + tracer.incrementCurrentSourceLine(buffer.countLines(start)); + Exprent initializer; if (fd.hasModifier(CodeConstants.ACC_STATIC)) { initializer = wrapper.getStaticFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); @@ -477,48 +589,72 @@ public class ClassWriter { } if (initializer != null) { if (isEnum && initializer.type == Exprent.EXPRENT_NEW) { - NewExprent nexpr = (NewExprent)initializer; - nexpr.setEnumconst(true); - buffer.append(nexpr.toJava(indent, tracer)); + NewExprent expr = (NewExprent)initializer; + expr.setEnumConst(true); + buffer.append(expr.toJava(indent, tracer)); } else { buffer.append(" = "); - // FIXME: special case field initializer. Can map to more than one method (constructor) and bytecode intruction. + + if (initializer.type == Exprent.EXPRENT_CONST) { + ((ConstExprent) initializer).adjustConstType(fieldType); + } + + // FIXME: special case field initializer. Can map to more than one method (constructor) and bytecode instruction buffer.append(initializer.toJava(indent, tracer)); } } else if (fd.hasModifier(CodeConstants.ACC_FINAL) && fd.hasModifier(CodeConstants.ACC_STATIC)) { - StructConstantValueAttribute attr = - (StructConstantValueAttribute)fd.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_CONSTANT_VALUE); + StructConstantValueAttribute attr = fd.getAttribute(StructGeneralAttribute.ATTRIBUTE_CONSTANT_VALUE); if (attr != null) { PrimitiveConstant constant = cl.getPool().getPrimitiveConstant(attr.getIndex()); buffer.append(" = "); - buffer.append(new ConstExprent(fieldType, constant.value).toJava(indent, tracer)); + buffer.append(new ConstExprent(fieldType, constant.value, null).toJava(indent, tracer)); } } if (!isEnum) { - buffer.append(";"); - buffer.append(lineSeparator); + buffer.append(";").appendLineSeparator(); + tracer.incrementCurrentSourceLine(); } } + 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, - ClassNode classNode, + ClassWrapper classWrapper, StructMethod mt, TextBuffer buffer, int indent, boolean codeOnly, BytecodeMappingTracer tracer) { - ClassWrapper classWrapper = classNode.wrapper; MethodWrapper methodWrapper = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); MethodWrapper outerWrapper = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methodWrapper); try { - String method_name = lambdaNode.lambda_information.method_name; - MethodDescriptor md_content = MethodDescriptor.parseDescriptor(lambdaNode.lambda_information.content_method_descriptor); - MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(lambdaNode.lambda_information.method_descriptor); + String method_name = lambdaNode.lambdaInformation.method_name; + MethodDescriptor md_content = MethodDescriptor.parseDescriptor(lambdaNode.lambdaInformation.content_method_descriptor); + MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(lambdaNode.lambdaInformation.method_descriptor); if (!codeOnly) { buffer.appendIndent(indent); @@ -527,7 +663,7 @@ public class ClassWriter { buffer.append("("); boolean firstParameter = true; - int index = lambdaNode.lambda_information.is_content_method_static ? 0 : 1; + int index = lambdaNode.lambdaInformation.is_content_method_static ? 0 : 1; int start_index = md_content.params.length - md_lambda.params.length; for (int i = 0; i < md_content.params.length; i++) { @@ -545,29 +681,29 @@ public class ClassWriter { buffer.append(typeName); buffer.append(" "); - String parameterName = methodWrapper.varproc.getVarName(new VarVersionPaar(index, 0, typeName, false)); // Spigot + String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0)); buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors firstParameter = false; } - index += md_content.params[i].stack_size; + index += md_content.params[i].stackSize; } - buffer.append(") {"); - buffer.append(DecompilerContext.getNewLineSeparator()); + buffer.append(") {").appendLineSeparator(); indent += 1; } + RootStatement root = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; if (!methodWrapper.decompiledWithErrors) { - RootStatement root = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; if (root != null) { // check for existence try { buffer.append(root.toJava(indent, tracer)); } - catch (Throwable ex) { - DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex); + catch (Throwable t) { + String message = "Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written."; + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t); methodWrapper.decompiledWithErrors = true; } } @@ -576,15 +712,16 @@ public class ClassWriter { if (methodWrapper.decompiledWithErrors) { buffer.appendIndent(indent); buffer.append("// $FF: Couldn't be decompiled"); - buffer.append(DecompilerContext.getNewLineSeparator()); + buffer.appendLineSeparator(); + } + + if (root != null) { + tracer.addMapping(root.getDummyExit().bytecode); } if (!codeOnly) { indent -= 1; - - buffer.appendIndent(indent); - buffer.append('}'); - buffer.append(DecompilerContext.getNewLineSeparator()); + buffer.appendIndent(indent).append('}').appendLineSeparator(); } } finally { @@ -592,16 +729,36 @@ public class ClassWriter { } } + private static String toValidJavaIdentifier(String name) { + if (name == null || name.isEmpty()) return name; + + boolean changed = false; + StringBuilder res = new StringBuilder(name.length()); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if ((i == 0 && !Character.isJavaIdentifierStart(c)) + || (i > 0 && !Character.isJavaIdentifierPart(c))) { + changed = true; + res.append("_"); + } + else { + res.append(c); + } + } + if (!changed) { + return name; + } + return res.append("/* $FF was: ").append(name).append("*/").toString(); + } + private boolean methodToJava(ClassNode node, StructMethod mt, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) { - ClassWrapper wrapper = node.wrapper; + ClassWrapper wrapper = node.getWrapper(); StructClass cl = wrapper.getClassStruct(); MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); boolean hideMethod = false; int start_index_method = buffer.length(); - String lineSeparator = DecompilerContext.getNewLineSeparator(); - MethodWrapper outerWrapper = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methodWrapper); @@ -609,17 +766,8 @@ public class ClassWriter { boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE); boolean isAnnotation = cl.hasModifier(CodeConstants.ACC_ANNOTATION); boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); - boolean isDeprecated = mt.getAttributes().containsKey("Deprecated"); - boolean clinit = false, init = false, dinit = false; - - int startLine = -1; - if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_LINE_NUMBERS)) { - StructLineNumberTableAttribute lineNumberTable = - (StructLineNumberTableAttribute)mt.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE); - if (lineNumberTable != null) { - startLine = lineNumberTable.getFirstLine(); - } - } + boolean isDeprecated = mt.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED); + boolean clInit = false, init = false, dInit = false; MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); @@ -627,65 +775,72 @@ public class ClassWriter { if ((flags & CodeConstants.ACC_NATIVE) != 0) { flags &= ~CodeConstants.ACC_STRICT; // compiler bug: a strictfp class sets all methods to strictfp } - if ("".equals(mt.getName())) { + if (CodeConstants.CLINIT_NAME.equals(mt.getName())) { flags &= CodeConstants.ACC_STATIC; // ignore all modifiers except 'static' in a static initializer } if (isDeprecated) { - appendDeprecation(buffer, indent, lineSeparator); + appendDeprecation(buffer, indent); } if (interceptor != null) { String oldName = interceptor.getOldName(cl.qualifiedName + " " + mt.getName() + " " + mt.getDescriptor()); - appendRenameComment(buffer, oldName, MType.METHOD, indent, lineSeparator); + appendRenameComment(buffer, oldName, MType.METHOD, indent); } - boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); + boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC); boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; if (isSynthetic) { - appendComment(buffer, "synthetic method", indent, lineSeparator); + return false; + //appendComment(buffer, "synthetic method", indent); } if (isBridge) { - appendComment(buffer, "bridge method", indent, lineSeparator); + appendComment(buffer, "bridge method", indent); } - appendAnnotations(buffer, mt, indent, lineSeparator); + appendAnnotations(buffer, indent, mt, TypeAnnotation.METHOD_RETURN_TYPE); buffer.appendIndent(indent); appendModifiers(buffer, flags, METHOD_ALLOWED, isInterface, METHOD_EXCLUDED); - if (isInterface && mt.containsCode()) { + if (isInterface && !mt.hasModifier(CodeConstants.ACC_STATIC) && mt.containsCode()) { // 'default' modifier (Java 8) buffer.append("default "); } String name = mt.getName(); - if ("".equals(name)) { + if (CodeConstants.INIT_NAME.equals(name)) { if (node.type == ClassNode.CLASS_ANONYMOUS) { name = ""; - dinit = true; + dInit = true; } else { name = node.simpleName; init = true; } } - else if ("".equals(name)) { + else if (CodeConstants.CLINIT_NAME.equals(name)) { name = ""; - clinit = true; + clInit = true; } GenericMethodDescriptor descriptor = null; if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { - StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)mt.getAttributes().getWithKey("Signature"); + StructGenericSignatureAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE); if (attr != null) { descriptor = GenericMain.parseMethodSignature(attr.getSignature()); if (descriptor != null) { - int actualParams = md.params.length; - if (isEnum && init) actualParams -= 2; - if (actualParams != descriptor.params.size()) { - String message = "Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor(); + long actualParams = md.params.length; + List mask = methodWrapper.synthParameters; + if (mask != null) { + actualParams = mask.stream().filter(Objects::isNull).count(); + } + else if (isEnum && init) { + actualParams -= 2; + } + if (actualParams != descriptor.parameterTypes.size()) { + String message = "Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor() + " in " + cl.qualifiedName; DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); descriptor = null; } @@ -696,17 +851,17 @@ 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.fparameters.isEmpty()) { - appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds); + if (descriptor != null && !descriptor.typeParameters.isEmpty()) { + appendTypeParameters(buffer, descriptor.typeParameters, descriptor.typeParameterBounds); buffer.append(' '); } if (!init) { if (descriptor != null) { - buffer.append(GenericMain.getGenericCastTypeName(descriptor.ret)); + buffer.append(GenericMain.getGenericCastTypeName(descriptor.returnType)); } else { buffer.append(ExprProcessor.getCastTypeName(md.ret)); @@ -714,95 +869,93 @@ public class ClassWriter { buffer.append(' '); } - buffer.append(name); + buffer.append(toValidJavaIdentifier(name)); buffer.append('('); - // parameters - List signFields = methodWrapper.signatureFields; + List mask = methodWrapper.synthParameters; int lastVisibleParameterIndex = -1; for (int i = 0; i < md.params.length; i++) { - if (signFields == null || signFields.get(i) == null) { + if (mask == null || mask.get(i) == null) { lastVisibleParameterIndex = i; } } - boolean firstParameter = true; + List methodParameters = null; + if (DecompilerContext.getOption(IFernflowerPreferences.USE_METHOD_PARAMETERS)) { + StructMethodParametersAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_METHOD_PARAMETERS); + if (attr != null) { + methodParameters = attr.getEntries(); + } + } + int index = isEnum && init ? 3 : thisVar ? 1 : 0; - int start = isEnum && init && descriptor == null ? 2 : 0; - int params = descriptor == null ? md.params.length : descriptor.params.size(); - for (int i = start; i < params; i++) { - // Spigot Start - String typeName; - boolean isVarArg; - // Spigot end - if (signFields == null || signFields.get(i) == null) { - if (!firstParameter) { + int start = isEnum && init ? 2 : 0; + for (int i = start; i < md.params.length; i++) { + if (mask == null || mask.get(i) == null) { + if (paramCount > 0) { buffer.append(", "); } appendParameterAnnotations(buffer, mt, paramCount); - if (methodWrapper.varproc.getVarFinal(new VarVersionPaar(index, 0)) == VarTypeProcessor.VAR_FINALEXPLICIT) { + if (methodParameters != null && i < methodParameters.size()) { + appendModifiers(buffer, methodParameters.get(i).myAccessFlags, CodeConstants.ACC_FINAL, isInterface, 0); + } + else if (methodWrapper.varproc.getVarFinal(new VarVersionPair(index, 0)) == VarTypeProcessor.VAR_EXPLICIT_FINAL) { buffer.append("final "); } + String typeName; + boolean isVarArg = i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS); + if (descriptor != null) { - GenericType parameterType = descriptor.params.get(i); - - isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arraydim > 0); // Spigot + GenericType parameterType = descriptor.parameterTypes.get(paramCount); + isVarArg &= parameterType.arrayDim > 0; if (isVarArg) { - parameterType.arraydim--; - } - - typeName = GenericMain.getGenericCastTypeName(parameterType); // Spigot - if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && - DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { - typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); - } - - buffer.append(typeName); - - if (isVarArg) { - buffer.append("..."); + parameterType = parameterType.decreaseArrayDim(); } + typeName = GenericMain.getGenericCastTypeName(parameterType); } else { - VarType parameterType = md.params[i].copy(); - - isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arraydim > 0); // Spigot + VarType parameterType = md.params[i]; + isVarArg &= parameterType.arrayDim > 0; if (isVarArg) { - parameterType.decArrayDim(); + parameterType = parameterType.decreaseArrayDim(); } + typeName = ExprProcessor.getCastTypeName(parameterType); + } - typeName = ExprProcessor.getCastTypeName(parameterType); // Spigot - if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && - DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { - typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); - } - - buffer.append(typeName); - - if (isVarArg) { - buffer.append("..."); - } + if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && + DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { + typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); + } + buffer.append(typeName); + if (isVarArg) { + buffer.append("..."); } buffer.append(' '); - String parameterName = methodWrapper.varproc.getVarName(new VarVersionPaar(index, 0, typeName, isVarArg)); // Spigot + + String parameterName; + if (methodParameters != null && i < methodParameters.size()) { + parameterName = methodParameters.get(i).myName; + } + else { + parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0)); + } buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors - firstParameter = false; paramCount++; } - index += md.params[i].stack_size; + index += md.params[i].stackSize; } buffer.append(')'); - StructExceptionsAttribute attr = (StructExceptionsAttribute)mt.getAttributes().getWithKey("Exceptions"); - if ((descriptor != null && !descriptor.exceptions.isEmpty()) || attr != null) { + StructExceptionsAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_EXCEPTIONS); + if ((descriptor != null && !descriptor.exceptionTypes.isEmpty()) || attr != null) { throwsExceptions = true; buffer.append(" throws "); @@ -810,8 +963,8 @@ public class ClassWriter { if (i > 0) { buffer.append(", "); } - if (descriptor != null && !descriptor.exceptions.isEmpty()) { - GenericType type = descriptor.exceptions.get(i); + if (descriptor != null && !descriptor.exceptionTypes.isEmpty()) { + GenericType type = descriptor.exceptionTypes.get(i); buffer.append(GenericMain.getGenericCastTypeName(type)); } else { @@ -822,49 +975,48 @@ public class ClassWriter { } } + tracer.incrementCurrentSourceLine(buffer.countLines(start_index_method)); + if ((flags & (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_NATIVE)) != 0) { // native or abstract method (explicit or interface) if (isAnnotation) { - StructAnnDefaultAttribute attr = (StructAnnDefaultAttribute)mt.getAttributes().getWithKey("AnnotationDefault"); + StructAnnDefaultAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_ANNOTATION_DEFAULT); if (attr != null) { buffer.append(" default "); - buffer.append(attr.getDefaultValue().toJava(indent + 1, new BytecodeMappingTracer())); // dummy tracer + buffer.append(attr.getDefaultValue().toJava(0, BytecodeMappingTracer.DUMMY)); } } buffer.append(';'); - buffer.append(lineSeparator); + buffer.appendLineSeparator(); } else { - if (!clinit && !dinit) { + if (!clInit && !dInit) { buffer.append(' '); } - //TODO: for now only start line set - buffer.setCurrentLine(startLine); - buffer.append('{'); + // We do not have line information for method start, lets have it here for now + buffer.append('{').appendLineSeparator(); + tracer.incrementCurrentSourceLine(); RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; - boolean empty = false; - if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence + if (root != null ) { // check for existence try { - tracer.incrementCurrentSourceLine(buffer.count(lineSeparator, start_index_method)); + // to restore in case of an exception + BytecodeMappingTracer codeTracer = new BytecodeMappingTracer(tracer.getCurrentSourceLine()); + TextBuffer code = root.toJava(indent + 1, codeTracer); - String code = root.toJava(indent + 1, tracer); + hideMethod = code.length() == 0 && (clInit || dInit || hideConstructor(node, init, throwsExceptions, paramCount, flags)) || + isSyntheticRecordMethod(cl, mt, code); - if (Util.rtrim(code).isEmpty()) code = ""; - - hideMethod = (clinit || dinit || hideConstructor(wrapper, init, throwsExceptions, paramCount)) && code.length() == 0; - - if (!code.isEmpty()) { - buffer.appendLineSeparator(); - } else { - empty = true; - } buffer.append(code); + + tracer.setCurrentSourceLine(codeTracer.getCurrentSourceLine()); + tracer.addTracer(codeTracer); } - catch (Throwable ex) { - DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex); + catch (Throwable t) { + String message = "Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written."; + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t); methodWrapper.decompiledWithErrors = true; } } @@ -872,14 +1024,16 @@ public class ClassWriter { if (methodWrapper.decompiledWithErrors) { buffer.appendIndent(indent + 1); buffer.append("// $FF: Couldn't be decompiled"); - buffer.append(lineSeparator); + buffer.appendLineSeparator(); + tracer.incrementCurrentSourceLine(); } - - if (!empty) { - buffer.appendIndent(indent); + else if (root != null) { + tracer.addMapping(root.getDummyExit().bytecode); } - buffer.append('}').appendLineSeparator(); + buffer.appendIndent(indent).append('}').appendLineSeparator(); } + + tracer.incrementCurrentSourceLine(); } finally { DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper); @@ -887,19 +1041,30 @@ public class ClassWriter { // save total lines // TODO: optimize - tracer.setCurrentSourceLine(buffer.count(lineSeparator, start_index_method)); + //tracer.setCurrentSourceLine(buffer.countLines(start_index_method)); return !hideMethod; } - private static boolean hideConstructor(ClassWrapper wrapper, boolean init, boolean throwsExceptions, int paramCount) { + 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; } + ClassWrapper wrapper = node.getWrapper(); + StructClass cl = wrapper.getClassStruct(); + + 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 && ((classAccessFlags & ACCESSIBILITY_FLAGS) != (methodAccessFlags & ACCESSIBILITY_FLAGS))) { + return false; + } + int count = 0; - for (StructMethod mt : wrapper.getClassStruct().getMethods()) { - if ("".equals(mt.getName())) { + for (StructMethod mt : cl.getMethods()) { + if (CodeConstants.INIT_NAME.equals(mt.getName())) { if (++count > 1) { return false; } @@ -909,13 +1074,27 @@ public class ClassWriter { return true; } - private static void appendDeprecation(TextBuffer buffer, int indent, String lineSeparator) { - buffer.appendIndent(indent).append("/** @deprecated */").append(lineSeparator); + 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(); } private enum MType {CLASS, FIELD, METHOD} - private static void appendRenameComment(TextBuffer buffer, String oldName, MType type, int indent, String lineSeparator) { + private static void appendRenameComment(TextBuffer buffer, String oldName, MType type, int indent) { if (oldName == null) return; buffer.appendIndent(indent); @@ -951,7 +1130,7 @@ public class ClassWriter { buffer.append(getTypePrintOut(md.ret)); } - buffer.append(lineSeparator); + buffer.appendLineSeparator(); } private static String getTypePrintOut(VarType type) { @@ -963,60 +1142,97 @@ public class ClassWriter { return typeText; } - private static void appendComment(TextBuffer buffer, String comment, int indent, String lineSeparator) { - // buffer.appendIndent(indent).append("// $FF: ").append(comment).append(lineSeparator); // Spigot: Squash comments + private static void appendComment(TextBuffer buffer, String comment, int indent) { + buffer.appendIndent(indent).append("// $FF: ").append(comment).appendLineSeparator(); } - private static final String[] ANNOTATION_ATTRIBUTES = { + private static final StructGeneralAttribute.Key[] ANNOTATION_ATTRIBUTES = { StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS}; + private static final StructGeneralAttribute.Key[] PARAMETER_ANNOTATION_ATTRIBUTES = { + StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; + private static final StructGeneralAttribute.Key[] TYPE_ANNOTATION_ATTRIBUTES = { + StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS}; - private static void appendAnnotations(TextBuffer buffer, StructMember mb, int indent, String lineSeparator) { + private static void appendAnnotations(TextBuffer buffer, int indent, StructMember mb, int targetType) { + Set filter = new HashSet<>(); - BytecodeMappingTracer tracer_dummy = new BytecodeMappingTracer(); // FIXME: replace with a real one - - for (String name : ANNOTATION_ATTRIBUTES) { - StructAnnotationAttribute attribute = (StructAnnotationAttribute)mb.getAttributes().getWithKey(name); + for (StructGeneralAttribute.Key key : ANNOTATION_ATTRIBUTES) { + StructAnnotationAttribute attribute = (StructAnnotationAttribute)mb.getAttribute(key); if (attribute != null) { for (AnnotationExprent annotation : attribute.getAnnotations()) { - buffer.append(annotation.toJava(indent, tracer_dummy)).append(lineSeparator); + String text = annotation.toJava(indent, BytecodeMappingTracer.DUMMY).toString(); + filter.add(text); + buffer.append(text); + if (indent < 0) { + buffer.append(' '); + } + else { + buffer.appendLineSeparator(); + } } } } + + appendTypeAnnotations(buffer, indent, mb, targetType, -1, filter); } - private static final String[] PARAMETER_ANNOTATION_ATTRIBUTES = { - StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; - private static void appendParameterAnnotations(TextBuffer buffer, StructMethod mt, int param) { + Set filter = new HashSet<>(); - BytecodeMappingTracer tracer_dummy = new BytecodeMappingTracer(); // FIXME: replace with a real one - - for (String name : PARAMETER_ANNOTATION_ATTRIBUTES) { - StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute)mt.getAttributes().getWithKey(name); + for (StructGeneralAttribute.Key key : PARAMETER_ANNOTATION_ATTRIBUTES) { + StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute)mt.getAttribute(key); if (attribute != null) { List> annotations = attribute.getParamAnnotations(); if (param < annotations.size()) { for (AnnotationExprent annotation : annotations.get(param)) { - buffer.append(annotation.toJava(0, tracer_dummy)).append(' '); + String text = annotation.toJava(-1, BytecodeMappingTracer.DUMMY).toString(); + filter.add(text); + buffer.append(text).append(' '); + } + } + } + } + + appendTypeAnnotations(buffer, -1, mt, TypeAnnotation.METHOD_PARAMETER, param, filter); + } + + private static void appendTypeAnnotations(TextBuffer buffer, int indent, StructMember mb, int targetType, int index, Set filter) { + for (StructGeneralAttribute.Key key : TYPE_ANNOTATION_ATTRIBUTES) { + StructTypeAnnotationAttribute attribute = (StructTypeAnnotationAttribute)mb.getAttribute(key); + if (attribute != null) { + for (TypeAnnotation annotation : attribute.getAnnotations()) { + if (annotation.isTopLevel() && annotation.getTargetType() == targetType && (index < 0 || annotation.getIndex() == index)) { + String text = annotation.getAnnotation().toJava(indent, BytecodeMappingTracer.DUMMY).toString(); + if (!filter.contains(text)) { + buffer.append(text); + if (indent < 0) { + buffer.append(' '); + } + else { + buffer.appendLineSeparator(); + } + } } } } } } - private static final Map MODIFIERS = new LinkedHashMap() {{ - put(CodeConstants.ACC_PUBLIC, "public"); - put(CodeConstants.ACC_PROTECTED, "protected"); - put(CodeConstants.ACC_PRIVATE, "private"); - put(CodeConstants.ACC_ABSTRACT, "abstract"); - put(CodeConstants.ACC_STATIC, "static"); - put(CodeConstants.ACC_FINAL, "final"); - put(CodeConstants.ACC_STRICT, "strictfp"); - put(CodeConstants.ACC_TRANSIENT, "transient"); - put(CodeConstants.ACC_VOLATILE, "volatile"); - put(CodeConstants.ACC_SYNCHRONIZED, "synchronized"); - put(CodeConstants.ACC_NATIVE, "native"); - }}; + private static final Map MODIFIERS; + static { + MODIFIERS = new LinkedHashMap<>(); + MODIFIERS.put(CodeConstants.ACC_PUBLIC, "public"); + MODIFIERS.put(CodeConstants.ACC_PROTECTED, "protected"); + MODIFIERS.put(CodeConstants.ACC_PRIVATE, "private"); + MODIFIERS.put(CodeConstants.ACC_ABSTRACT, "abstract"); + MODIFIERS.put(CodeConstants.ACC_STATIC, "static"); + MODIFIERS.put(CodeConstants.ACC_FINAL, "final"); + MODIFIERS.put(CodeConstants.ACC_STRICT, "strictfp"); + MODIFIERS.put(CodeConstants.ACC_TRANSIENT, "transient"); + MODIFIERS.put(CodeConstants.ACC_VOLATILE, "volatile"); + MODIFIERS.put(CodeConstants.ACC_SYNCHRONIZED, "synchronized"); + MODIFIERS.put(CodeConstants.ACC_NATIVE, "native"); + } private static final int CLASS_ALLOWED = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE | CodeConstants.ACC_ABSTRACT | @@ -1026,12 +1242,15 @@ public class ClassWriter { CodeConstants.ACC_FINAL | CodeConstants.ACC_TRANSIENT | CodeConstants.ACC_VOLATILE; private static final int METHOD_ALLOWED = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE | CodeConstants.ACC_ABSTRACT | - CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL | CodeConstants.ACC_SYNCHRONIZED | CodeConstants.ACC_NATIVE | CodeConstants.ACC_STRICT; + CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL | CodeConstants.ACC_SYNCHRONIZED | CodeConstants.ACC_NATIVE | + CodeConstants.ACC_STRICT; private static final int CLASS_EXCLUDED = CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_STATIC; private static final int FIELD_EXCLUDED = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL; private static final int METHOD_EXCLUDED = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_ABSTRACT; + private static final int ACCESSIBILITY_FLAGS = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE; + private static void appendModifiers(TextBuffer buffer, int flags, int allowed, boolean isInterface, int excluded) { flags &= allowed; if (!isInterface) excluded = 0; @@ -1042,7 +1261,17 @@ public class ClassWriter { } } - private static void appendTypeParameters(TextBuffer buffer, List parameters, List> bounds) { + public static GenericClassDescriptor getGenericClassDescriptor(StructClass cl) { + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute attr = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE); + if (attr != null) { + return GenericMain.parseClassSignature(attr.getSignature()); + } + } + return null; + } + + public static void appendTypeParameters(TextBuffer buffer, List parameters, List> bounds) { buffer.append('<'); for (int i = 0; i < parameters.size(); i++) { @@ -1065,4 +1294,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 987c1a5..7bb50aa 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -1,23 +1,10 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; +import org.jetbrains.java.decompiler.code.Instruction; +import org.jetbrains.java.decompiler.code.InstructionSequence; import org.jetbrains.java.decompiler.main.collectors.BytecodeSourceMapper; -import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.collectors.ImportCollector; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; @@ -27,109 +14,103 @@ import org.jetbrains.java.decompiler.main.rels.LambdaProcessor; import org.jetbrains.java.decompiler.main.rels.NestedClassProcessor; import org.jetbrains.java.decompiler.main.rels.NestedMemberAccess; import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.attr.StructEnclosingMethodAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.io.IOException; import java.util.*; import java.util.Map.Entry; -public class ClassesProcessor { - +public class ClassesProcessor implements CodeConstants { public static final int AVERAGE_CLASS_SIZE = 16 * 1024; - private Map mapRootClasses = new HashMap(); + private final StructContext context; + private final Map mapRootClasses = new HashMap<>(); + + private static class Inner { + private String simpleName; + private int type; + private int accessFlags; + + private static boolean equal(Inner o1, Inner o2) { + return o1.type == o2.type && o1.accessFlags == o2.accessFlags && InterpreterUtil.equalObjects(o1.simpleName, o2.simpleName); + } + } public ClassesProcessor(StructContext context) { + this.context = context; + } - HashMap mapInnerClasses = new HashMap(); - HashMap> mapNestedClassReferences = new HashMap>(); - HashMap> mapEnclosingClassReferences = new HashMap>(); - HashMap mapNewSimpleNames = new HashMap(); + public void loadClasses(IIdentifierRenamer renamer) { + Map mapInnerClasses = new HashMap<>(); + Map> mapNestedClassReferences = new HashMap<>(); + Map> mapEnclosingClassReferences = new HashMap<>(); + Map mapNewSimpleNames = new HashMap<>(); boolean bDecompileInner = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_INNER); + boolean verifyAnonymousClasses = DecompilerContext.getOption(IFernflowerPreferences.VERIFY_ANONYMOUS_CLASSES); // create class nodes for (StructClass cl : context.getClasses().values()) { if (cl.isOwn() && !mapRootClasses.containsKey(cl.qualifiedName)) { - if (bDecompileInner) { - StructInnerClassesAttribute inner = (StructInnerClassesAttribute)cl.getAttributes().getWithKey("InnerClasses"); + StructInnerClassesAttribute inner = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES); + if (inner != null) { - - for (int i = 0; i < inner.getClassEntries().size(); i++) { - - int[] entry = inner.getClassEntries().get(i); - String[] strentry = inner.getStringEntries().get(i); - - Object[] arr = new Object[4]; // arr[0] not used - - String innername = strentry[0]; - - // nested class type - arr[2] = entry[1] == 0 ? (entry[2] == 0 ? ClassNode.CLASS_ANONYMOUS : ClassNode.CLASS_LOCAL) : ClassNode.CLASS_MEMBER; + for (StructInnerClassesAttribute.Entry entry : inner.getEntries()) { + String innerName = entry.innerName; // original simple name - String simpleName = strentry[2]; - String savedName = mapNewSimpleNames.get(innername); - + String simpleName = entry.simpleName; + String savedName = mapNewSimpleNames.get(innerName); if (savedName != null) { simpleName = savedName; } - else if (simpleName != null && DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) { - IIdentifierRenamer renamer = DecompilerContext.getPoolInterceptor().getHelper(); - if (renamer.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, simpleName, null, null)) { - simpleName = renamer.getNextClassname(innername, simpleName); - mapNewSimpleNames.put(innername, simpleName); - } + else if (simpleName != null && + renamer != null && + renamer.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_CLASS, simpleName, null, null)) { + simpleName = renamer.getNextClassName(innerName, simpleName); + mapNewSimpleNames.put(innerName, simpleName); } - arr[1] = simpleName; - - // original access flags - arr[3] = entry[3]; + Inner rec = new Inner(); + rec.simpleName = simpleName; + rec.type = entry.simpleNameIdx == 0 ? ClassNode.CLASS_ANONYMOUS : entry.outerNameIdx == 0 ? ClassNode.CLASS_LOCAL : ClassNode.CLASS_MEMBER; + rec.accessFlags = entry.accessFlags; // enclosing class - String enclClassName; - if (entry[1] != 0) { - enclClassName = strentry[1]; + String enclClassName = entry.outerNameIdx != 0 ? entry.enclosingName : cl.qualifiedName; + if (enclClassName == null || innerName.equals(enclClassName)) { + continue; // invalid name or self reference } - else { - enclClassName = cl.qualifiedName; + if (rec.type == ClassNode.CLASS_MEMBER && !innerName.equals(enclClassName + '$' + entry.simpleName)) { + continue; // not a real inner class } - if (!innername.equals(enclClassName)) { // self reference - StructClass enclosing_class = context.getClasses().get(enclClassName); - if (enclosing_class != null && enclosing_class.isOwn()) { // own classes only - - Object[] arrold = mapInnerClasses.get(innername); - if (arrold == null) { - mapInnerClasses.put(innername, arr); - } - else if (!InterpreterUtil.equalObjectArrays(arrold, arr)) { - String message = "Inconsistent inner class entries for " + innername + "!"; - DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); - } - - // reference to the nested class - HashSet set = mapNestedClassReferences.get(enclClassName); - if (set == null) { - mapNestedClassReferences.put(enclClassName, set = new HashSet()); - } - set.add(innername); - - // reference to the enclosing class - set = mapEnclosingClassReferences.get(innername); - if (set == null) { - mapEnclosingClassReferences.put(innername, set = new HashSet()); - } - set.add(enclClassName); + StructClass enclosingClass = context.getClasses().get(enclClassName); + if (enclosingClass != null && enclosingClass.isOwn()) { // own classes only + Inner existingRec = mapInnerClasses.get(innerName); + if (existingRec == null) { + mapInnerClasses.put(innerName, rec); } + else if (!Inner.equal(existingRec, rec)) { + String message = "Inconsistent inner class entries for " + innerName + "!"; + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); + } + + // reference to the nested class + mapNestedClassReferences.computeIfAbsent(enclClassName, k -> new HashSet<>()).add(innerName); + // reference to the enclosing class + mapEnclosingClassReferences.computeIfAbsent(innerName, k -> new HashSet<>()).add(enclClassName); } } } @@ -142,30 +123,32 @@ public class ClassesProcessor { } if (bDecompileInner) { - // connect nested classes for (Entry ent : mapRootClasses.entrySet()) { // root class? if (!mapInnerClasses.containsKey(ent.getKey())) { - - HashSet setVisited = new HashSet(); - LinkedList stack = new LinkedList(); + Set setVisited = new HashSet<>(); + LinkedList stack = new LinkedList<>(); stack.add(ent.getKey()); setVisited.add(ent.getKey()); while (!stack.isEmpty()) { - String superClass = stack.removeFirst(); - ClassNode supernode = mapRootClasses.get(superClass); + ClassNode superNode = mapRootClasses.get(superClass); - HashSet setNestedClasses = mapNestedClassReferences.get(superClass); + Set setNestedClasses = mapNestedClassReferences.get(superClass); if (setNestedClasses != null) { + StructClass scl = superNode.classStruct; + StructInnerClassesAttribute inner = scl.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES); - StructClass scl = supernode.classStruct; - StructInnerClassesAttribute inner = (StructInnerClassesAttribute)scl.getAttributes().getWithKey("InnerClasses"); - for (int i = 0; i < inner.getStringEntries().size(); i++) { - String nestedClass = inner.getStringEntries().get(i)[0]; + if (inner == null || inner.getEntries().isEmpty()) { + DecompilerContext.getLogger().writeMessage(superClass + " does not contain inner classes!", IFernflowerLogger.Severity.WARN); + continue; + } + + for (StructInnerClassesAttribute.Entry entry : inner.getEntries()) { + String nestedClass = entry.innerName; if (!setNestedClasses.contains(nestedClass)) { continue; } @@ -174,52 +157,49 @@ public class ClassesProcessor { continue; } - ClassNode nestednode = mapRootClasses.get(nestedClass); - if (nestednode == null) { + ClassNode nestedNode = mapRootClasses.get(nestedClass); + if (nestedNode == null) { DecompilerContext.getLogger().writeMessage("Nested class " + nestedClass + " missing!", IFernflowerLogger.Severity.WARN); continue; } - Object[] arr = mapInnerClasses.get(nestedClass); + Inner rec = mapInnerClasses.get(nestedClass); //if ((Integer)arr[2] == ClassNode.CLASS_MEMBER) { // FIXME: check for consistent naming //} - nestednode.type = (Integer)arr[2]; - nestednode.simpleName = (String)arr[1]; - nestednode.access = (Integer)arr[3]; + nestedNode.simpleName = rec.simpleName; + nestedNode.type = rec.type; + nestedNode.access = rec.accessFlags; - if (nestednode.type == ClassNode.CLASS_ANONYMOUS) { - StructClass cl = nestednode.classStruct; + // sanity checks of the class supposed to be anonymous + if (verifyAnonymousClasses && nestedNode.type == ClassNode.CLASS_ANONYMOUS && !isAnonymous(nestedNode.classStruct, scl)) { + nestedNode.type = ClassNode.CLASS_LOCAL; + } - // remove static if anonymous class - // a common compiler bug - nestednode.access &= ~CodeConstants.ACC_STATIC; + if (nestedNode.type == ClassNode.CLASS_ANONYMOUS) { + StructClass cl = nestedNode.classStruct; + // remove static if anonymous class (a common compiler bug) + nestedNode.access &= ~CodeConstants.ACC_STATIC; int[] interfaces = cl.getInterfaces(); - if (interfaces.length > 0) { - if (interfaces.length > 1) { - String message = "Inconsistent anonymous class definition: " + cl.qualifiedName; - DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); - } - nestednode.anonymousClassType = new VarType(cl.getInterface(0), true); + nestedNode.anonymousClassType = new VarType(cl.getInterface(0), true); } else { - nestednode.anonymousClassType = new VarType(cl.superClass.getString(), true); + nestedNode.anonymousClassType = new VarType(cl.superClass.getString(), true); } } - else if (nestednode.type == ClassNode.CLASS_LOCAL) { - // only abstract and final are permitted - // a common compiler bug - nestednode.access &= (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_FINAL); + else if (nestedNode.type == ClassNode.CLASS_LOCAL) { + // only abstract and final are permitted (a common compiler bug) + nestedNode.access &= (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_FINAL); } - supernode.nested.add(nestednode); - nestednode.parent = supernode; + superNode.nested.add(nestedNode); + nestedNode.parent = superNode; - nestednode.enclosingClasses.addAll(mapEnclosingClassReferences.get(nestedClass)); + nestedNode.enclosingClasses.addAll(mapEnclosingClassReferences.get(nestedClass)); stack.add(nestedClass); } @@ -230,64 +210,158 @@ public class ClassesProcessor { } } + private static boolean isAnonymous(StructClass cl, StructClass enclosingCl) { + // checking super class and interfaces + int[] interfaces = cl.getInterfaces(); + if (interfaces.length > 0) { + boolean hasNonTrivialSuperClass = cl.superClass != null && !VarType.VARTYPE_OBJECT.equals(new VarType(cl.superClass.getString(), true)); + if (hasNonTrivialSuperClass || interfaces.length > 1) { // can't have multiple 'sources' + String message = "Inconsistent anonymous class definition: '" + cl.qualifiedName + "'. Multiple interfaces and/or super class defined."; + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); + return false; + } + } + else if (cl.superClass == null) { // neither interface nor super class defined + String message = "Inconsistent anonymous class definition: '" + cl.qualifiedName + "'. Neither interface nor super class defined."; + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); + return false; + } + + // FIXME: check constructors + // FIXME: check enclosing class/method + + ConstantPool pool = enclosingCl.getPool(); + + int refCounter = 0; + boolean refNotNew = false; + + StructEnclosingMethodAttribute attribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_ENCLOSING_METHOD); + String enclosingMethod = attribute != null ? attribute.getMethodName() : null; + + // checking references in the enclosing class + for (StructMethod mt : enclosingCl.getMethods()) { + if (enclosingMethod != null && !enclosingMethod.equals(mt.getName())) { + continue; + } + + try { + mt.expandData(enclosingCl); + + InstructionSequence seq = mt.getInstructionSequence(); + if (seq != null) { + int len = seq.length(); + for (int i = 0; i < len; i++) { + Instruction instr = seq.getInstr(i); + switch (instr.opcode) { + case opc_checkcast: + case opc_instanceof: + if (cl.qualifiedName.equals(pool.getPrimitiveConstant(instr.operand(0)).getString())) { + refCounter++; + refNotNew = true; + } + break; + case opc_new: + case opc_anewarray: + case opc_multianewarray: + if (cl.qualifiedName.equals(pool.getPrimitiveConstant(instr.operand(0)).getString())) { + refCounter++; + } + break; + case opc_getstatic: + case opc_putstatic: + if (cl.qualifiedName.equals(pool.getLinkConstant(instr.operand(0)).classname)) { + refCounter++; + refNotNew = true; + } + } + } + } + + mt.releaseResources(); + } + catch (IOException ex) { + String message = "Could not read method while checking anonymous class definition: '" + enclosingCl.qualifiedName + "', '" + + InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()) + "'"; + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); + return false; + } + + if (refCounter > 1 || refNotNew) { + String message = "Inconsistent references to the class '" + cl.qualifiedName + "' which is supposed to be anonymous"; + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); + return false; + } + } + + return true; + } + public void writeClass(StructClass cl, TextBuffer buffer) throws IOException { ClassNode root = mapRootClasses.get(cl.qualifiedName); if (root.type != ClassNode.CLASS_ROOT) { 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.setImportCollector(importCollector); - DecompilerContext.setCounterContainer(new CounterContainer()); - DecompilerContext.setBytecodeSourceMapper(new BytecodeSourceMapper()); + DecompilerContext.startClass(importCollector); - new LambdaProcessor().processClass(root); + if (packageInfo) { + ClassWriter.packageInfoToJava(cl, buffer); - // add simple class names to implicit import - addClassnameToImport(root, importCollector); - - // build wrappers for all nested classes (that's where actual processing takes place) - initWrappers(root); - - new NestedClassProcessor().processClass(root, root); - - new NestedMemberAccess().propagateMemberAccess(root); - - TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE); - new ClassWriter().classToJava(root, classBuffer, 0); - - String lineSeparator = DecompilerContext.getNewLineSeparator(); - int total_offset_lines = 0; - - int index = cl.qualifiedName.lastIndexOf("/"); - if (index >= 0) { - total_offset_lines+=2; - String packageName = cl.qualifiedName.substring(0, index).replace('/', '.'); - - buffer.append("package "); - buffer.append(packageName); - buffer.append(";"); - buffer.append(lineSeparator); - buffer.append(lineSeparator); + importCollector.writeImports(buffer, false); } + else if (moduleInfo) { + TextBuffer moduleBuffer = new TextBuffer(AVERAGE_CLASS_SIZE); + ClassWriter.moduleInfoToJava(cl, moduleBuffer); - int import_lines_written = importCollector.writeImports(buffer); - if (import_lines_written > 0) { - buffer.append(lineSeparator); - total_offset_lines += import_lines_written + 1; + importCollector.writeImports(buffer, true); + + buffer.append(moduleBuffer); } - //buffer.append(lineSeparator); + else { + new LambdaProcessor().processClass(root); - buffer.append(classBuffer); + // add simple class names to implicit import + addClassNameToImport(root, importCollector); - if(DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) { - BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper(); - mapper.addTotalOffset(total_offset_lines); + // build wrappers for all nested classes (that's where actual processing takes place) + initWrappers(root); - buffer.append(lineSeparator); - mapper.dumpMapping(buffer); + new NestedClassProcessor().processClass(root, root); + + new NestedMemberAccess().propagateMemberAccess(root); + + TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE); + new ClassWriter().classToJava(root, classBuffer, 0, null); + + int index = cl.qualifiedName.lastIndexOf('/'); + if (index >= 0) { + String packageName = cl.qualifiedName.substring(0, index).replace('/', '.'); + buffer.append("package ").append(packageName).append(';').appendLineSeparator().appendLineSeparator(); + } + + importCollector.writeImports(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); + } + } } } finally { @@ -296,8 +370,7 @@ public class ClassesProcessor { } } - private static void initWrappers(ClassNode node) throws IOException { - + private static void initWrappers(ClassNode node) { if (node.type == ClassNode.CLASS_LAMBDA) { return; } @@ -312,19 +385,17 @@ public class ClassesProcessor { } } - 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); } } private static void destroyWrappers(ClassNode node) { - node.wrapper = null; node.classStruct.releaseResources(); @@ -339,7 +410,6 @@ public class ClassesProcessor { public static class ClassNode { - public static final int CLASS_ROOT = 0; public static final int CLASS_MEMBER = 1; public static final int CLASS_ANONYMOUS = 2; @@ -347,30 +417,18 @@ public class ClassesProcessor { public static final int CLASS_LAMBDA = 8; public int type; - public int access; - public String simpleName; - - public StructClass classStruct; - - public ClassWrapper wrapper; - + public final StructClass classStruct; + private ClassWrapper wrapper; public String enclosingMethod; - public InvocationExprent superInvocation; - - public HashMap mapFieldsToVars = new HashMap(); - + public final Map mapFieldsToVars = new HashMap<>(); public VarType anonymousClassType; - - public List nested = new ArrayList(); - - public Set enclosingClasses = new HashSet(); - + public final List nested = new ArrayList<>(); + public final Set enclosingClasses = new HashSet<>(); public ClassNode parent; - - public LambdaInformation lambda_information; + public LambdaInformation lambdaInformation; public ClassNode(String content_class_name, String content_method_name, @@ -383,19 +441,18 @@ public class ClassesProcessor { this.type = CLASS_LAMBDA; this.classStruct = classStruct; // 'parent' class containing the static function - lambda_information = new LambdaInformation(); + lambdaInformation = new LambdaInformation(); - lambda_information.class_name = lambda_class_name; - lambda_information.method_name = lambda_method_name; - lambda_information.method_descriptor = lambda_method_descriptor; + lambdaInformation.method_name = lambda_method_name; + lambdaInformation.method_descriptor = lambda_method_descriptor; - lambda_information.content_class_name = content_class_name; - lambda_information.content_method_name = content_method_name; - lambda_information.content_method_descriptor = content_method_descriptor; - lambda_information.content_method_invocation_type = content_method_invocation_type; + lambdaInformation.content_class_name = content_class_name; + lambdaInformation.content_method_name = content_method_name; + lambdaInformation.content_method_descriptor = content_method_descriptor; + lambdaInformation.content_method_invocation_type = content_method_invocation_type; - lambda_information.content_method_key = - InterpreterUtil.makeUniqueKey(lambda_information.content_method_name, lambda_information.content_method_descriptor); + lambdaInformation.content_method_key = + InterpreterUtil.makeUniqueKey(lambdaInformation.content_method_name, lambdaInformation.content_method_descriptor); anonymousClassType = new VarType(lambda_class_name, true); @@ -405,9 +462,9 @@ public class ClassesProcessor { is_method_reference = !mt.isSynthetic(); // if not synthetic -> method reference } - lambda_information.is_method_reference = is_method_reference; - lambda_information.is_content_method_static = - (lambda_information.content_method_invocation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic); // FIXME: redundant? + lambdaInformation.is_method_reference = is_method_reference; + lambdaInformation.is_content_method_static = + (lambdaInformation.content_method_invocation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic); // FIXME: redundant? } public ClassNode(int type, StructClass classStruct) { @@ -426,8 +483,15 @@ public class ClassesProcessor { return null; } + public ClassWrapper getWrapper() { + ClassNode node = this; + while (node.type == CLASS_LAMBDA) { + node = node.parent; + } + return node.wrapper; + } + public static class LambdaInformation { - public String class_name; public String method_name; public String method_descriptor; @@ -435,11 +499,10 @@ public class ClassesProcessor { public String content_method_name; public String content_method_descriptor; public int content_method_invocation_type; // values from CONSTANT_MethodHandle_REF_* - public String content_method_key; public boolean is_method_reference; public boolean is_content_method_static; } } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/DecompilerContext.java b/src/org/jetbrains/java/decompiler/main/DecompilerContext.java index f1fddac..e293635 100644 --- a/src/org/jetbrains/java/decompiler/main/DecompilerContext.java +++ b/src/org/jetbrains/java/decompiler/main/DecompilerContext.java @@ -1,64 +1,57 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main; import org.jetbrains.java.decompiler.main.collectors.BytecodeSourceMapper; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.collectors.ImportCollector; -import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; import org.jetbrains.java.decompiler.struct.StructContext; -import java.util.HashMap; -import java.util.Locale; import java.util.Map; +import java.util.Objects; public class DecompilerContext { public static final String CURRENT_CLASS = "CURRENT_CLASS"; + public static final String CURRENT_CLASS_WRAPPER = "CURRENT_CLASS_WRAPPER"; public static final String CURRENT_CLASS_NODE = "CURRENT_CLASS_NODE"; - public static final String CURRENT_METHOD = "CURRENT_METHOD"; - public static final String CURRENT_METHOD_DESCRIPTOR = "CURRENT_METHOD_DESCRIPTOR"; public static final String CURRENT_METHOD_WRAPPER = "CURRENT_METHOD_WRAPPER"; - public static final String CURRENT_VAR_PROCESSOR = "CURRENT_VAR_PROCESSOR"; - - private static ThreadLocal currentContext = new ThreadLocal(); private final Map properties; - private StructContext structContext; + private final IFernflowerLogger logger; + private final StructContext structContext; + private final ClassesProcessor classProcessor; + private final PoolInterceptor poolInterceptor; private ImportCollector importCollector; - private VarNamesCollector varNamescollector; + private VarProcessor varProcessor; private CounterContainer counterContainer; - private ClassesProcessor classProcessor; - private PoolInterceptor poolInterceptor; - private IFernflowerLogger logger; private BytecodeSourceMapper bytecodeSourceMapper; - private DecompilerContext(Map properties) { + public DecompilerContext(Map properties, + IFernflowerLogger logger, + StructContext structContext, + ClassesProcessor classProcessor, + PoolInterceptor interceptor) { + Objects.requireNonNull(properties); + Objects.requireNonNull(logger); + Objects.requireNonNull(structContext); + Objects.requireNonNull(classProcessor); + this.properties = properties; + this.logger = logger; + this.structContext = structContext; + this.classProcessor = classProcessor; + this.poolInterceptor = interceptor; + this.counterContainer = new CounterContainer(); } - public static void initContext(Map propertiesCustom) { - Map properties = new HashMap(IFernflowerPreferences.DEFAULTS); - if (propertiesCustom != null) { - properties.putAll(propertiesCustom); - } - currentContext.set(new DecompilerContext(properties)); - } + // ***************************************************************************** + // context setup and update + // ***************************************************************************** + + private static final ThreadLocal currentContext = new ThreadLocal<>(); public static DecompilerContext getCurrentContext() { return currentContext.get(); @@ -68,93 +61,69 @@ public class DecompilerContext { currentContext.set(context); } - public static Object getProperty(String key) { - return getCurrentContext().properties.get(key); - } - public static void setProperty(String key, Object value) { getCurrentContext().properties.put(key, value); } + public static void startClass(ImportCollector importCollector) { + DecompilerContext context = getCurrentContext(); + context.importCollector = importCollector; + context.counterContainer = new CounterContainer(); + context.bytecodeSourceMapper = new BytecodeSourceMapper(); + } + + public static void startMethod(VarProcessor varProcessor) { + DecompilerContext context = getCurrentContext(); + context.varProcessor = varProcessor; + context.counterContainer = new CounterContainer(); + } + + // ***************************************************************************** + // context access + // ***************************************************************************** + + public static Object getProperty(String key) { + return getCurrentContext().properties.get(key); + } + public static boolean getOption(String key) { - return "1".equals(getCurrentContext().properties.get(key)); + return "1".equals(getProperty(key)); } - public static ImportCollector getImportCollector() { - return getCurrentContext().importCollector; - } - - public static void setImportCollector(ImportCollector importCollector) { - getCurrentContext().importCollector = importCollector; - } - - public static VarNamesCollector getVarNamesCollector() { - return getCurrentContext().varNamescollector; - } - - public static void setVarNamesCollector(VarNamesCollector varNamesCollector) { - getCurrentContext().varNamescollector = varNamesCollector; - } - - public static StructContext getStructContext() { - return getCurrentContext().structContext; - } - - public static void setStructContext(StructContext structContext) { - getCurrentContext().structContext = structContext; - } - - public static CounterContainer getCounterContainer() { - return getCurrentContext().counterContainer; - } - - public static void setCounterContainer(CounterContainer counterContainer) { - getCurrentContext().counterContainer = counterContainer; - } - - public static ClassesProcessor getClassProcessor() { - return getCurrentContext().classProcessor; - } - - public static void setClassProcessor(ClassesProcessor classProcessor) { - getCurrentContext().classProcessor = classProcessor; - } - - public static PoolInterceptor getPoolInterceptor() { - return getCurrentContext().poolInterceptor; - } - - public static void setPoolInterceptor(PoolInterceptor poolinterceptor) { - getCurrentContext().poolInterceptor = poolinterceptor; - } - - public static BytecodeSourceMapper getBytecodeSourceMapper() { - return getCurrentContext().bytecodeSourceMapper; - } - - public static void setBytecodeSourceMapper(BytecodeSourceMapper bytecodeSourceMapper) { - getCurrentContext().bytecodeSourceMapper = bytecodeSourceMapper; + public static String getNewLineSeparator() { + return getOption(IFernflowerPreferences.NEW_LINE_SEPARATOR) ? + IFernflowerPreferences.LINE_SEPARATOR_UNX : IFernflowerPreferences.LINE_SEPARATOR_WIN; } public static IFernflowerLogger getLogger() { return getCurrentContext().logger; } - public static void setLogger(IFernflowerLogger logger) { - if (logger != null) { - String level = (String)getProperty(IFernflowerPreferences.LOG_LEVEL); - if (level != null) { - try { - logger.setSeverity(IFernflowerLogger.Severity.valueOf(level.toUpperCase(Locale.US))); - } - catch (IllegalArgumentException ignore) { } - } - } - getCurrentContext().logger = logger; + public static StructContext getStructContext() { + return getCurrentContext().structContext; } - public static String getNewLineSeparator() { - return getOption(IFernflowerPreferences.NEW_LINE_SEPARATOR) ? - IFernflowerPreferences.LINE_SEPARATOR_LIN : IFernflowerPreferences.LINE_SEPARATOR_WIN; + public static ClassesProcessor getClassProcessor() { + return getCurrentContext().classProcessor; } -} + + public static PoolInterceptor getPoolInterceptor() { + return getCurrentContext().poolInterceptor; + } + + public static ImportCollector getImportCollector() { + return getCurrentContext().importCollector; + } + + public static VarProcessor getVarProcessor() { + return getCurrentContext().varProcessor; + } + + public static CounterContainer getCounterContainer() { + return getCurrentContext().counterContainer; + } + + public static BytecodeSourceMapper getBytecodeSourceMapper() { + return getCurrentContext().bytecodeSourceMapper; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/EnumProcessor.java b/src/org/jetbrains/java/decompiler/main/EnumProcessor.java index be408e8..7f6beef 100644 --- a/src/org/jetbrains/java/decompiler/main/EnumProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/EnumProcessor.java @@ -1,34 +1,19 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; import org.jetbrains.java.decompiler.main.rels.ClassWrapper; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statements; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.util.InterpreterUtil; -public class EnumProcessor { - +public final class EnumProcessor { public static void clearEnum(ClassWrapper wrapper) { StructClass cl = wrapper.getClassStruct(); @@ -48,13 +33,13 @@ public class EnumProcessor { wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(name, descriptor)); } } - else if ("".equals(name)) { - Statement firstData = findFirstData(method.root); + else if (CodeConstants.INIT_NAME.equals(name)) { + Statement firstData = Statements.findFirstData(method.root); if (firstData != null && !firstData.getExprents().isEmpty()) { Exprent exprent = firstData.getExprents().get(0); if (exprent.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent invexpr = (InvocationExprent)exprent; - if (isInvocationSuperConstructor(invexpr, method, wrapper)) { + InvocationExprent invExpr = (InvocationExprent)exprent; + if (Statements.isInvocationInitConstructor(invExpr, method, wrapper, false)) { firstData.getExprents().remove(0); } } @@ -70,49 +55,4 @@ public class EnumProcessor { } } } - - // FIXME: move to a util class (see also InitializerProcessor) - private static Statement findFirstData(Statement stat) { - - if (stat.getExprents() != null) { - return stat; - } - else { - if (stat.isLabeled()) { - return null; - } - - switch (stat.type) { - case Statement.TYPE_SEQUENCE: - case Statement.TYPE_IF: - case Statement.TYPE_ROOT: - case Statement.TYPE_SWITCH: - case Statement.TYPE_SYNCRONIZED: - return findFirstData(stat.getFirst()); - default: - return null; - } - } - } - - // FIXME: move to util class (see also InitializerProcessor) - private static boolean isInvocationSuperConstructor(InvocationExprent inv, MethodWrapper meth, ClassWrapper wrapper) { - - if (inv.getFunctype() == InvocationExprent.TYP_INIT) { - if (inv.getInstance().type == Exprent.EXPRENT_VAR) { - VarExprent instvar = (VarExprent)inv.getInstance(); - VarVersionPaar varpaar = new VarVersionPaar(instvar); - - String classname = meth.varproc.getThisvars().get(varpaar); - - if (classname != null) { // any this instance. TODO: Restrict to current class? - if (!wrapper.getClassStruct().qualifiedName.equals(inv.getClassname())) { - return true; - } - } - } - } - - return false; - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/Fernflower.java b/src/org/jetbrains/java/decompiler/main/Fernflower.java index 85d9b00..692c1b6 100644 --- a/src/org/jetbrains/java/decompiler/main/Fernflower.java +++ b/src/org/jetbrains/java/decompiler/main/Fernflower.java @@ -1,55 +1,88 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; -import org.jetbrains.java.decompiler.main.collectors.CounterContainer; -import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; -import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; -import org.jetbrains.java.decompiler.main.extern.IResultSaver; -import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.main.extern.*; +import org.jetbrains.java.decompiler.modules.renamer.ConverterHelper; import org.jetbrains.java.decompiler.modules.renamer.IdentifierConverter; +import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; import org.jetbrains.java.decompiler.struct.IDecompiledData; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; +import org.jetbrains.java.decompiler.util.TextBuffer; +import java.io.File; +import java.util.HashMap; +import java.util.Locale; import java.util.Map; public class Fernflower implements IDecompiledData { + private final StructContext structContext; + private final ClassesProcessor classProcessor; + private final IIdentifierRenamer helper; + private final IdentifierConverter converter; - private StructContext structContext; - private ClassesProcessor classesProcessor; + public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map customProperties, IFernflowerLogger logger) { + Map properties = new HashMap<>(IFernflowerPreferences.DEFAULTS); + if (customProperties != null) { + properties.putAll(customProperties); + } + + String level = (String)properties.get(IFernflowerPreferences.LOG_LEVEL); + if (level != null) { + try { + logger.setSeverity(IFernflowerLogger.Severity.valueOf(level.toUpperCase(Locale.ENGLISH))); + } + catch (IllegalArgumentException ignore) { } + } - public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map options, IFernflowerLogger logger) { structContext = new StructContext(saver, this, new LazyLoader(provider)); - DecompilerContext.initContext(options); - DecompilerContext.setCounterContainer(new CounterContainer()); - DecompilerContext.setLogger(logger); + classProcessor = new ClassesProcessor(structContext); + + PoolInterceptor interceptor = null; + if ("1".equals(properties.get(IFernflowerPreferences.RENAME_ENTITIES))) { + helper = loadHelper((String)properties.get(IFernflowerPreferences.USER_RENAMER_CLASS), logger); + interceptor = new PoolInterceptor(); + converter = new IdentifierConverter(structContext, helper, interceptor); + } + else { + helper = null; + converter = null; + } + + DecompilerContext context = new DecompilerContext(properties, logger, structContext, classProcessor, interceptor); + DecompilerContext.setCurrentContext(context); + } + + private static IIdentifierRenamer loadHelper(String className, IFernflowerLogger logger) { + if (className != null) { + try { + Class renamerClass = Fernflower.class.getClassLoader().loadClass(className); + return (IIdentifierRenamer) renamerClass.getDeclaredConstructor().newInstance(); + } + catch (Exception e) { + logger.writeMessage("Cannot load renamer '" + className + "'", IFernflowerLogger.Severity.WARN, e); + } + } + + return new ConverterHelper(); + } + + public void addSource(File source) { + structContext.addSpace(source, true); + } + + public void addLibrary(File library) { + structContext.addSpace(library, false); } public void decompileContext() { - if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) { - new IdentifierConverter().rename(structContext); + if (converter != null) { + converter.rename(); } - classesProcessor = new ClassesProcessor(structContext); - - DecompilerContext.setClassProcessor(classesProcessor); - DecompilerContext.setStructContext(structContext); + classProcessor.loadClasses(helper); structContext.saveContext(); } @@ -58,24 +91,18 @@ public class Fernflower implements IDecompiledData { DecompilerContext.setCurrentContext(null); } - public StructContext getStructContext() { - return structContext; - } - @Override public String getClassEntryName(StructClass cl, String entryName) { - ClassNode node = classesProcessor.getMapRootClasses().get(cl.qualifiedName); + ClassNode node = classProcessor.getMapRootClasses().get(cl.qualifiedName); if (node.type != ClassNode.CLASS_ROOT) { return null; } + else if (converter != null) { + String simpleClassName = cl.qualifiedName.substring(cl.qualifiedName.lastIndexOf('/') + 1); + return entryName.substring(0, entryName.lastIndexOf('/') + 1) + simpleClassName + ".java"; + } else { - if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) { - String simple_classname = cl.qualifiedName.substring(cl.qualifiedName.lastIndexOf('/') + 1); - return entryName.substring(0, entryName.lastIndexOf('/') + 1) + simple_classname + ".java"; - } - else { - return entryName.substring(0, entryName.lastIndexOf(".class")) + ".java"; - } + return entryName.substring(0, entryName.lastIndexOf(".class")) + ".java"; } } @@ -84,12 +111,12 @@ public class Fernflower implements IDecompiledData { try { TextBuffer buffer = new TextBuffer(ClassesProcessor.AVERAGE_CLASS_SIZE); buffer.append(DecompilerContext.getProperty(IFernflowerPreferences.BANNER).toString()); - classesProcessor.writeClass(cl, buffer); - return org.spigotmc.fernflower.EclipseFormatter.format(buffer.toString()); // Spigot + classProcessor.writeClass(cl, buffer); + return buffer.toString(); } - catch (Throwable ex) { - DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", ex); + catch (Throwable t) { + DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", t); return null; } } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java b/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java index 4c3f625..d3696dd 100644 --- a/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -23,7 +9,8 @@ import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statements; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.util.InterpreterUtil; @@ -31,14 +18,11 @@ 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 meth = wrapper.getMethodWrapper("", "()V"); - if (meth != null && meth.root != null) { // successfully decompiled static constructor - extractStaticInitializers(wrapper, meth); + MethodWrapper method = wrapper.getMethodWrapper(CodeConstants.CLINIT_NAME, "()V"); + if (method != null && method.root != null) { // successfully decompiled static constructor + extractStaticInitializers(wrapper, method); } extractDynamicInitializers(wrapper); @@ -52,30 +36,26 @@ public class InitializerProcessor { } } - private static void liftConstructor(ClassWrapper wrapper) { - - for (MethodWrapper meth : wrapper.getMethods()) { - if ("".equals(meth.methodStruct.getName()) && meth.root != null) { - Statement firstdata = findFirstData(meth.root); - if (firstdata == null) { + for (MethodWrapper method : wrapper.getMethods()) { + if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) { + Statement firstData = Statements.findFirstData(method.root); + if (firstData == null) { return; } - int index = 0; - List lstExprents = firstdata.getExprents(); + List lstExprents = firstData.getExprents(); for (Exprent exprent : lstExprents) { - int action = 0; if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asexpr = (AssignmentExprent)exprent; - if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) { - FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); - if (fexpr.getClassname().equals(wrapper.getClassStruct().qualifiedName)) { - StructField structField = wrapper.getClassStruct().getField(fexpr.getName(), fexpr.getDescriptor().descriptorString); + AssignmentExprent assignExpr = (AssignmentExprent)exprent; + if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD && assignExpr.getRight().type == Exprent.EXPRENT_VAR) { + FieldExprent fExpr = (FieldExprent)assignExpr.getLeft(); + if (fExpr.getClassname().equals(wrapper.getClassStruct().qualifiedName)) { + StructField structField = wrapper.getClassStruct().getField(fExpr.getName(), fExpr.getDescriptor().descriptorString); if (structField != null && structField.hasModifier(CodeConstants.ACC_FINAL)) { action = 1; } @@ -83,7 +63,7 @@ public class InitializerProcessor { } } else if (index > 0 && exprent.type == Exprent.EXPRENT_INVOCATION && - isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, true)) { + Statements.isInvocationInitConstructor((InvocationExprent)exprent, method, wrapper, true)) { // this() or super() lstExprents.add(0, lstExprents.remove(index)); action = 2; @@ -99,52 +79,50 @@ public class InitializerProcessor { } } - private static void hideEmptySuper(ClassWrapper wrapper) { - - for (MethodWrapper meth : wrapper.getMethods()) { - if ("".equals(meth.methodStruct.getName()) && meth.root != null) { - Statement firstdata = findFirstData(meth.root); - if (firstdata == null || firstdata.getExprents().isEmpty()) { + for (MethodWrapper method : wrapper.getMethods()) { + if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) { + Statement firstData = Statements.findFirstData(method.root); + if (firstData == null || firstData.getExprents().isEmpty()) { return; } - Exprent exprent = firstdata.getExprents().get(0); + Exprent exprent = firstData.getExprents().get(0); if (exprent.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent invexpr = (InvocationExprent)exprent; - if (isInvocationInitConstructor(invexpr, meth, wrapper, false) && invexpr.getLstParameters().isEmpty()) { - firstdata.getExprents().remove(0); + InvocationExprent invExpr = (InvocationExprent)exprent; + if (Statements.isInvocationInitConstructor(invExpr, method, wrapper, false) && invExpr.getLstParameters().isEmpty()) { + firstData.getExprents().remove(0); } } } } } - private static void extractStaticInitializers(ClassWrapper wrapper, MethodWrapper meth) { - - RootStatement root = meth.root; + private static void extractStaticInitializers(ClassWrapper wrapper, MethodWrapper method) { + RootStatement root = method.root; StructClass cl = wrapper.getClassStruct(); + Statement firstData = Statements.findFirstData(root); + if (firstData != null) { + boolean inlineInitializers = cl.hasModifier(CodeConstants.ACC_INTERFACE) || cl.hasModifier(CodeConstants.ACC_ENUM); - Statement firstdata = findFirstData(root); - if (firstdata != null) { - while (!firstdata.getExprents().isEmpty()) { - Exprent exprent = firstdata.getExprents().get(0); + while (!firstData.getExprents().isEmpty()) { + Exprent exprent = firstData.getExprents().get(0); boolean found = false; if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asexpr = (AssignmentExprent)exprent; - if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { - FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); - if (fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) && - cl.hasField(fexpr.getName(), fexpr.getDescriptor().descriptorString)) { + AssignmentExprent assignExpr = (AssignmentExprent)exprent; + if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) { + FieldExprent fExpr = (FieldExprent)assignExpr.getLeft(); + if (fExpr.isStatic() && fExpr.getClassname().equals(cl.qualifiedName) && + cl.hasField(fExpr.getName(), fExpr.getDescriptor().descriptorString)) { - if (true || isExprentIndependent(asexpr.getRight(), meth)) { // Spigot - - String keyField = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString); + // interfaces fields should always be initialized inline + if (inlineInitializers || isExprentIndependent(assignExpr.getRight(), method)) { + String keyField = InterpreterUtil.makeUniqueKey(fExpr.getName(), fExpr.getDescriptor().descriptorString); if (!wrapper.getStaticFieldInitializers().containsKey(keyField)) { - wrapper.getStaticFieldInitializers().addWithKey(asexpr.getRight(), keyField); - firstdata.getExprents().remove(0); + wrapper.getStaticFieldInitializers().addWithKey(assignExpr.getRight(), keyField); + firstData.getExprents().remove(0); found = true; } } @@ -160,27 +138,26 @@ public class InitializerProcessor { } private static void extractDynamicInitializers(ClassWrapper wrapper) { - StructClass cl = wrapper.getClassStruct(); boolean isAnonymous = DecompilerContext.getClassProcessor().getMapRootClasses().get(cl.qualifiedName).type == ClassNode.CLASS_ANONYMOUS; - List> lstFirst = new ArrayList>(); - List lstMethWrappers = new ArrayList(); + List> lstFirst = new ArrayList<>(); + List lstMethodWrappers = new ArrayList<>(); - for (MethodWrapper meth : wrapper.getMethods()) { - if ("".equals(meth.methodStruct.getName()) && meth.root != null) { // successfully decompiled constructor - Statement firstdata = findFirstData(meth.root); - if (firstdata == null || firstdata.getExprents().isEmpty()) { + for (MethodWrapper method : wrapper.getMethods()) { + if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) { // successfully decompiled constructor + Statement firstData = Statements.findFirstData(method.root); + if (firstData == null || firstData.getExprents().isEmpty()) { return; } - lstFirst.add(firstdata.getExprents()); - lstMethWrappers.add(meth); + lstFirst.add(firstData.getExprents()); + lstMethodWrappers.add(method); - Exprent exprent = firstdata.getExprents().get(0); + Exprent exprent = firstData.getExprents().get(0); if (!isAnonymous) { // FIXME: doesn't make sense if (exprent.type != Exprent.EXPRENT_INVOCATION || - !isInvocationInitConstructor((InvocationExprent)exprent, meth, wrapper, false)) { + !Statements.isInvocationInitConstructor((InvocationExprent)exprent, method, wrapper, false)) { return; } } @@ -192,12 +169,10 @@ public class InitializerProcessor { } while (true) { - String fieldWithDescr = null; Exprent value = null; for (int i = 0; i < lstFirst.size(); i++) { - List lst = lstFirst.get(i); if (lst.size() < (isAnonymous ? 1 : 2)) { @@ -209,22 +184,21 @@ public class InitializerProcessor { boolean found = false; if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asexpr = (AssignmentExprent)exprent; - if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { - FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); - if (!fexpr.isStatic() && fexpr.getClassname().equals(cl.qualifiedName) && - cl.hasField(fexpr.getName(), fexpr - .getDescriptor().descriptorString)) { // check for the physical existence of the field. Could be defined in a superclass. + AssignmentExprent assignExpr = (AssignmentExprent)exprent; + if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) { + FieldExprent fExpr = (FieldExprent)assignExpr.getLeft(); + if (!fExpr.isStatic() && fExpr.getClassname().equals(cl.qualifiedName) && + cl.hasField(fExpr.getName(), fExpr.getDescriptor().descriptorString)) { // check for the physical existence of the field. Could be defined in a superclass. - if (isExprentIndependent(asexpr.getRight(), lstMethWrappers.get(i))) { - String fieldKey = InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString); + if (isExprentIndependent(assignExpr.getRight(), lstMethodWrappers.get(i))) { + String fieldKey = InterpreterUtil.makeUniqueKey(fExpr.getName(), fExpr.getDescriptor().descriptorString); if (fieldWithDescr == null) { fieldWithDescr = fieldKey; - value = asexpr.getRight(); + value = assignExpr.getRight(); } else { if (!fieldWithDescr.equals(fieldKey) || - !value.equals(asexpr.getRight())) { + !value.equals(assignExpr.getRight())) { return; } } @@ -252,19 +226,17 @@ public class InitializerProcessor { } } - private static boolean isExprentIndependent(Exprent exprent, MethodWrapper meth) { - + private static boolean isExprentIndependent(Exprent exprent, MethodWrapper method) { List lst = exprent.getAllExprents(true); lst.add(exprent); for (Exprent expr : lst) { switch (expr.type) { case Exprent.EXPRENT_VAR: - VarVersionPaar varpaar = new VarVersionPaar((VarExprent)expr); - if (!meth.varproc.getExternvars().contains(varpaar)) { - String varname = meth.varproc.getVarName(varpaar); - - if (!varname.equals("this") && !varname.endsWith(".this")) { // FIXME: remove direct comparison with strings + VarVersionPair varPair = new VarVersionPair((VarExprent)expr); + if (!method.varproc.getExternalVars().contains(varPair)) { + String varName = method.varproc.getVarName(varPair); + if (!varName.equals("this") && !varName.endsWith(".this")) { // FIXME: remove direct comparison with strings return false; } } @@ -276,48 +248,4 @@ public class InitializerProcessor { return true; } - - - private static Statement findFirstData(Statement stat) { - - if (stat.getExprents() != null) { - return stat; - } - else { - if (stat.isLabeled()) { // FIXME: Why?? - return null; - } - - switch (stat.type) { - case Statement.TYPE_SEQUENCE: - case Statement.TYPE_IF: - case Statement.TYPE_ROOT: - case Statement.TYPE_SWITCH: - case Statement.TYPE_SYNCRONIZED: - return findFirstData(stat.getFirst()); - default: - return null; - } - } - } - - private static boolean isInvocationInitConstructor(InvocationExprent inv, MethodWrapper meth, ClassWrapper wrapper, boolean withThis) { - - if (inv.getFunctype() == InvocationExprent.TYP_INIT) { - if (inv.getInstance().type == Exprent.EXPRENT_VAR) { - VarExprent instvar = (VarExprent)inv.getInstance(); - VarVersionPaar varpaar = new VarVersionPaar(instvar); - - String classname = meth.varproc.getThisvars().get(varpaar); - - if (classname != null) { // any this instance. TODO: Restrict to current class? - if (withThis || !wrapper.getClassStruct().qualifiedName.equals(inv.getClassname())) { - return true; - } - } - } - } - - return false; - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/TextBuffer.java b/src/org/jetbrains/java/decompiler/main/TextBuffer.java deleted file mode 100644 index 7dd3d2f..0000000 --- a/src/org/jetbrains/java/decompiler/main/TextBuffer.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.main; - -import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * Allows to connect text with resulting lines - * - * @author egor - */ -public class TextBuffer { - private final String myLineSeparator = DecompilerContext.getNewLineSeparator(); - private final String myIndent = (String)DecompilerContext.getProperty(IFernflowerPreferences.INDENT_STRING); - private final StringBuilder myStringBuilder; - private Map myLineToOffsetMapping = null; - - public TextBuffer() { - myStringBuilder = new StringBuilder(); - } - - public TextBuffer(int size) { - myStringBuilder = new StringBuilder(size); - } - - public void setCurrentLine(int line) { - if (line >= 0) { - checkMapCreated(); - myLineToOffsetMapping.put(line, myStringBuilder.length()+1); - } - } - - public TextBuffer append(String str) { - myStringBuilder.append(str); - return this; - } - - public TextBuffer append(char ch) { - myStringBuilder.append(ch); - return this; - } - - public TextBuffer appendLineSeparator() { - myStringBuilder.append(myLineSeparator); - return this; - } - - public TextBuffer appendIndent(int length) { - while (length-- > 0) { - append(myIndent); - } - return this; - } - - public TextBuffer addBanner(String banner) { - myStringBuilder.insert(0, banner); - if (myLineToOffsetMapping != null) { - for (Integer line : myLineToOffsetMapping.keySet()) { - myLineToOffsetMapping.put(line, myLineToOffsetMapping.get(line) + banner.length()); - } - } - return this; - } - - @Override - public String toString() { - String original = myStringBuilder.toString(); - if (myLineToOffsetMapping == null || myLineToOffsetMapping.isEmpty()) { - return original; - } - else { - StringBuilder res = new StringBuilder(); - String[] srcLines = original.split(myLineSeparator); - int currentLineStartOffset = 0; - int currentLine = 0; - int previousMarkLine = 0; - int dumpedLines = 0; - ArrayList linesWithMarks = new ArrayList(myLineToOffsetMapping.keySet()); - Collections.sort(linesWithMarks); - for (Integer markLine : linesWithMarks) { - Integer markOffset = myLineToOffsetMapping.get(markLine); - while (currentLine < srcLines.length) { - String line = srcLines[currentLine]; - int lineEnd = currentLineStartOffset + line.length() + myLineSeparator.length(); - if (markOffset >= currentLineStartOffset && markOffset <= lineEnd) { - int requiredLinesNumber = markLine - dumpedLines; - dumpedLines = markLine; - appendLines(res, srcLines, previousMarkLine, currentLine, requiredLinesNumber); - previousMarkLine = currentLine; - break; - } - currentLineStartOffset = lineEnd; - currentLine++; - } - } - if (previousMarkLine < srcLines.length) { - appendLines(res, srcLines, previousMarkLine, srcLines.length, srcLines.length - previousMarkLine); - } - - return res.toString(); - } - } - - private void appendLines(StringBuilder res, String[] srcLines, int from, int to, int requiredLineNumber) { - if (to - from > requiredLineNumber) { - int separatorsRequired = to - from - requiredLineNumber - 1; - for (int i = from; i < to; i++) { - res.append(srcLines[i]); - if (separatorsRequired-- > 0) { - res.append(myLineSeparator); - } - } - res.append(myLineSeparator); - } - else if (to - from <= requiredLineNumber) { - for (int i = from; i < to; i++) { - res.append(srcLines[i]).append(myLineSeparator); - } - for (int i = 0; i < requiredLineNumber - to + from; i++) { - res.append(myLineSeparator); - } - } - } - - public int length() { - return myStringBuilder.length(); - } - - public String substring(int start) { - return myStringBuilder.substring(start); - } - - public void setLength(int position) { - myStringBuilder.setLength(position); - } - - public TextBuffer append(TextBuffer buffer) { - if (buffer.myLineToOffsetMapping != null && !buffer.myLineToOffsetMapping.isEmpty()) { - checkMapCreated(); - for (Map.Entry entry : buffer.myLineToOffsetMapping.entrySet()) { - myLineToOffsetMapping.put(entry.getKey(), entry.getValue() + myStringBuilder.length()); - } - } - myStringBuilder.append(buffer.myStringBuilder); - return this; - } - - private void checkMapCreated() { - if (myLineToOffsetMapping == null) { - myLineToOffsetMapping = new HashMap(); - } - } - - public void insert(int offset, String s) { - if (myLineToOffsetMapping != null) { - throw new IllegalStateException("insert not yet supported with Line mapping"); - } - myStringBuilder.insert(offset, s); - } - - public int count(String substring, int from) { - int count = 0, length = substring.length(), p = from; - while ((p = myStringBuilder.indexOf(substring, p)) > 0) { - ++count; - p += length; - } - return count; - } -} diff --git a/src/org/jetbrains/java/decompiler/main/collectors/BytecodeMappingTracer.java b/src/org/jetbrains/java/decompiler/main/collectors/BytecodeMappingTracer.java index 20af427..c6a23b3 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/BytecodeMappingTracer.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/BytecodeMappingTracer.java @@ -1,70 +1,103 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.collectors; -import java.util.HashMap; +import org.jetbrains.java.decompiler.struct.attr.StructLineNumberTableAttribute; + +import java.util.*; import java.util.Map.Entry; -import java.util.Set; public class BytecodeMappingTracer { + public static final BytecodeMappingTracer DUMMY = new BytecodeMappingTracer(); - private int current_sourceline; + private int currentSourceLine; + private StructLineNumberTableAttribute lineNumberTable = null; + private final Map mapping = new HashMap<>(); // bytecode offset, source line - // bytecode offset, source line - private HashMap mapping = new HashMap(); - - public BytecodeMappingTracer() {} + public BytecodeMappingTracer() { } public BytecodeMappingTracer(int initial_source_line) { - current_sourceline = initial_source_line; + currentSourceLine = initial_source_line; } public void incrementCurrentSourceLine() { - current_sourceline++; + currentSourceLine++; } public void incrementCurrentSourceLine(int number_lines) { - current_sourceline += number_lines; - } - - public void shiftSourceLines(int shift) { - for(Entry entry : mapping.entrySet()) { - entry.setValue(entry.getValue() + shift); - } + currentSourceLine += number_lines; } public void addMapping(int bytecode_offset) { - if(!mapping.containsKey(bytecode_offset)) { - mapping.put(bytecode_offset, current_sourceline); - } + mapping.putIfAbsent(bytecode_offset, currentSourceLine); } public void addMapping(Set bytecode_offsets) { - if(bytecode_offsets != null) { - for(Integer bytecode_offset : bytecode_offsets) { + if (bytecode_offsets != null) { + for (Integer bytecode_offset : bytecode_offsets) { addMapping(bytecode_offset); } } } public void addTracer(BytecodeMappingTracer tracer) { - if(tracer != null) { - for(Entry entry : tracer.mapping.entrySet()) { - if(!mapping.containsKey(entry.getKey())) { - mapping.put(entry.getKey(), entry.getValue()); - } + if (tracer != null) { + for (Entry entry : tracer.mapping.entrySet()) { + mapping.putIfAbsent(entry.getKey(), entry.getValue()); } } } - public HashMap getMapping() { + public Map getMapping() { return mapping; } public int getCurrentSourceLine() { - return current_sourceline; + return currentSourceLine; } - public void setCurrentSourceLine(int current_sourceline) { - this.current_sourceline = current_sourceline; + public void setCurrentSourceLine(int currentSourceLine) { + this.currentSourceLine = currentSourceLine; } -} + public void setLineNumberTable(StructLineNumberTableAttribute lineNumberTable) { + this.lineNumberTable = lineNumberTable; + } + + private final Set unmappedLines = new HashSet<>(); + + public Set getUnmappedLines() { + return unmappedLines; + } + + public Map getOriginalLinesMapping() { + if (lineNumberTable == null) { + return Collections.emptyMap(); + } + + Map res = new HashMap<>(); + + // first match offsets from line number table + int[] data = lineNumberTable.getRawData(); + for (int i = 0; i < data.length; i += 2) { + int originalOffset = data[i]; + int originalLine = data[i + 1]; + Integer newLine = mapping.get(originalOffset); + if (newLine != null) { + res.put(originalLine, newLine); + } + else { + unmappedLines.add(originalLine); + } + } + + // now match offsets from decompiler mapping + for (Entry entry : mapping.entrySet()) { + int originalLine = lineNumberTable.findLineNumber(entry.getKey()); + if (originalLine > -1 && !res.containsKey(originalLine)) { + res.put(originalLine, entry.getValue()); + unmappedLines.remove(originalLine); + } + } + return res; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/collectors/BytecodeSourceMapper.java b/src/org/jetbrains/java/decompiler/main/collectors/BytecodeSourceMapper.java index 7bf4f08..67fb04f 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/BytecodeSourceMapper.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/BytecodeSourceMapper.java @@ -1,81 +1,109 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.collectors; import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.TextBuffer; +import org.jetbrains.java.decompiler.util.TextBuffer; -import java.util.HashMap; +import java.util.*; import java.util.Map.Entry; public class BytecodeSourceMapper { - private int offset_total; // class, method, bytecode offset, source line - private HashMap>> mapping = new HashMap>>(); + private final Map>> mapping = new LinkedHashMap<>(); - public void addMapping(String classname, String methodname, int bytecode_offset, int source_line) { + // original line to decompiled line + private final Map linesMapping = new HashMap<>(); + private final Set unmappedLines = new TreeSet<>(); - HashMap> class_mapping = mapping.get(classname); - if(class_mapping == null) { - mapping.put(classname, class_mapping = new HashMap>()); - } - - HashMap method_mapping = class_mapping.get(methodname); - if(method_mapping == null) { - class_mapping.put(methodname, method_mapping = new HashMap()); - } + public void addMapping(String className, String methodName, int bytecodeOffset, int sourceLine) { + Map> class_mapping = mapping.computeIfAbsent(className, k -> new LinkedHashMap<>()); // need to preserve order + Map method_mapping = class_mapping.computeIfAbsent(methodName, k -> new HashMap<>()); // don't overwrite - if(!method_mapping.containsKey(bytecode_offset)) { - method_mapping.put(bytecode_offset, source_line); - } + method_mapping.putIfAbsent(bytecodeOffset, sourceLine); } - public void addTracer(String classname, String methodname, BytecodeMappingTracer tracer) { - for(Entry entry : tracer.getMapping().entrySet()) { - addMapping(classname, methodname, entry.getKey(), entry.getValue()); + public void addTracer(String className, String methodName, BytecodeMappingTracer tracer) { + for (Entry entry : tracer.getMapping().entrySet()) { + addMapping(className, methodName, entry.getKey(), entry.getValue()); } + linesMapping.putAll(tracer.getOriginalLinesMapping()); + unmappedLines.addAll(tracer.getUnmappedLines()); } - public void dumpMapping(TextBuffer buffer) { + public void dumpMapping(TextBuffer buffer, boolean offsetsToHex) { + if (mapping.isEmpty() && linesMapping.isEmpty()) { + return; + } String lineSeparator = DecompilerContext.getNewLineSeparator(); - for(Entry>> class_entry : mapping.entrySet()) { - HashMap> class_mapping = class_entry.getValue(); - buffer.append("class " + class_entry.getKey() + "{" + lineSeparator); + for (Entry>> class_entry : mapping.entrySet()) { + Map> class_mapping = class_entry.getValue(); + buffer.append("class '" + class_entry.getKey() + "' {" + lineSeparator); boolean is_first_method = true; + for (Entry> method_entry : class_mapping.entrySet()) { + Map method_mapping = method_entry.getValue(); - for(Entry> method_entry : class_mapping.entrySet()) { - HashMap method_mapping = method_entry.getValue(); - - if(!is_first_method) { + if (!is_first_method) { buffer.appendLineSeparator(); } - buffer.appendIndent(1).append("method " + method_entry.getKey() + "{" + lineSeparator); - for(Entry line : method_mapping.entrySet()) { - buffer.appendIndent(2).append(line.getKey().toString()).appendIndent(2).append((line.getValue() + offset_total) + lineSeparator); + buffer.appendIndent(1).append("method '" + method_entry.getKey() + "' {" + lineSeparator); + + List lstBytecodeOffsets = new ArrayList<>(method_mapping.keySet()); + Collections.sort(lstBytecodeOffsets); + + for (Integer offset : lstBytecodeOffsets) { + Integer line = method_mapping.get(offset); + + String strOffset = offsetsToHex ? Integer.toHexString(offset) : line.toString(); + buffer.appendIndent(2).append(strOffset).appendIndent(2).append((line + offset_total) + lineSeparator); } buffer.appendIndent(1).append("}").appendLineSeparator(); + is_first_method = false; } - buffer.append("}").appendLineSeparator(); + + buffer.append("}").appendLineSeparator().appendLineSeparator(); } - } - public int getTotalOffset() { - return offset_total; - } + // lines mapping + buffer.append("Lines mapping:").appendLineSeparator(); + Map sorted = new TreeMap<>(linesMapping); + for (Entry entry : sorted.entrySet()) { + buffer.append(entry.getKey()).append(" <-> ").append(entry.getValue() + offset_total + 1).appendLineSeparator(); + } - public void setTotalOffset(int offset_total) { - this.offset_total = offset_total; + if (!unmappedLines.isEmpty()) { + buffer.append("Not mapped:").appendLineSeparator(); + for (Integer line : unmappedLines) { + if (!linesMapping.containsKey(line)) { + buffer.append(line).appendLineSeparator(); + } + } + } } public void addTotalOffset(int offset_total) { this.offset_total += offset_total; } - -} + /** + * Original to decompiled line mapping. + */ + public int[] getOriginalLinesMapping() { + int[] res = new int[linesMapping.size() * 2]; + int i = 0; + for (Entry entry : linesMapping.entrySet()) { + res[i] = entry.getKey(); + unmappedLines.remove(entry.getKey()); + res[i + 1] = entry.getValue() + offset_total + 1; // make it 1 based + i += 2; + } + return res; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java b/src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java index 04d50a7..7091694 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java @@ -1,27 +1,12 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.collectors; public class CounterContainer { - public static final int STATEMENT_COUNTER = 0; - public static final int EXPRENT_COUNTER = 1; + public static final int EXPRESSION_COUNTER = 1; public static final int VAR_COUNTER = 2; - private int[] values = new int[]{1, 1, 1}; + private final int[] values = new int[]{1, 1, 1}; public void setCounter(int counter, int value) { values[counter] = value; @@ -34,4 +19,4 @@ public class CounterContainer { public int getCounterAndIncrement(int counter) { return values[counter]++; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java index fae600a..dc6b3a5 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java @@ -1,155 +1,186 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.TextBuffer; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute; +import org.jetbrains.java.decompiler.util.TextBuffer; +import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructContext; +import org.jetbrains.java.decompiler.struct.StructField; import java.util.*; -import java.util.Map.Entry; - +import java.util.stream.Collectors; public class ImportCollector { - private static final String JAVA_LANG_PACKAGE = "java.lang"; - private Map mapSimpleNames = new HashMap(); - private Set setNotImportedNames = new HashSet(); - private String currentPackageSlash = ""; - private String currentPackagePoint = ""; + private final Map mapSimpleNames = new HashMap<>(); + private final Set setNotImportedNames = new HashSet<>(); + // set of field names in this class and all its predecessors. + private final Set setFieldNames = new HashSet<>(); + private final Set setInnerClassNames = new HashSet<>(); + private final String currentPackageSlash; + private final String currentPackagePoint; public ImportCollector(ClassNode root) { - - String clname = root.classStruct.qualifiedName; - int index = clname.lastIndexOf("/"); + String clName = root.classStruct.qualifiedName; + int index = clName.lastIndexOf('/'); if (index >= 0) { - currentPackageSlash = clname.substring(0, index); - currentPackagePoint = currentPackageSlash.replace('/', '.'); - currentPackageSlash += "/"; + String packageName = clName.substring(0, index); + currentPackageSlash = packageName + '/'; + currentPackagePoint = packageName.replace('/', '.'); + } + else { + currentPackageSlash = ""; + currentPackagePoint = ""; + } + + Map 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()); + } + + Collections.addAll(queue, currentClass.getInterfaceNames()); + + // all field names for the current class .. + for (StructField f : currentClass.getFields()) { + setFieldNames.add(f.getName()); + } + + // .. all inner classes for the current class .. + StructInnerClassesAttribute attribute = currentClass.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES); + if (attribute != null) { + for (StructInnerClassesAttribute.Entry entry : attribute.getEntries()) { + if (entry.enclosingName != null && entry.enclosingName.equals(currentClass.qualifiedName)) { + setInnerClassNames.add(entry.simpleName); + } + } + } + + // .. and traverse through parent. + 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()); } } - public String getShortName(String fullname) { - return getShortName(fullname, true); + /** + * Check whether the package-less name ClassName is shaded by variable in a context of + * the decompiled class + * @param classToName - pkg.name.ClassName - class to find shortname for + * @return ClassName if the name is not shaded by local field, pkg.name.ClassName otherwise + */ + public String getShortNameInClassContext(String classToName) { + String shortName = getShortName(classToName); + if (setFieldNames.contains(shortName)) { + return classToName; + } + else { + return shortName; + } } - public String getShortName(String fullname, boolean imported) { + public String getShortName(String fullName) { + return getShortName(fullName, true); + } - ClassesProcessor clproc = DecompilerContext.getClassProcessor(); - ClassNode node = clproc.getMapRootClasses().get(fullname.replace('.', '/')); - - String retname = null; + public String getShortName(String fullName, boolean imported) { + ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(fullName.replace('.', '/')); //todo[r.sh] anonymous classes? + String result = null; if (node != null && node.classStruct.isOwn()) { - - retname = node.simpleName; + result = node.simpleName; while (node.parent != null && node.type == ClassNode.CLASS_MEMBER) { - retname = node.parent.simpleName + "." + retname; + //noinspection StringConcatenationInLoop + result = node.parent.simpleName + '.' + result; node = node.parent; } if (node.type == ClassNode.CLASS_ROOT) { - fullname = node.classStruct.qualifiedName; - fullname = fullname.replace('/', '.'); + fullName = node.classStruct.qualifiedName; + fullName = fullName.replace('/', '.'); } else { - return retname; + return result; } } else { - fullname = fullname.replace('$', '.'); + fullName = fullName.replace('$', '.'); } - String nshort = fullname; - String npackage = ""; + String shortName = fullName; + String packageName = ""; - int lastpoint = fullname.lastIndexOf("."); - - if (lastpoint >= 0) { - nshort = fullname.substring(lastpoint + 1); - npackage = fullname.substring(0, lastpoint); + int lastDot = fullName.lastIndexOf('.'); + if (lastDot >= 0) { + shortName = fullName.substring(lastDot + 1); + packageName = fullName.substring(0, lastDot); } StructContext context = DecompilerContext.getStructContext(); - boolean existsDefaultClass = (context.getClass(currentPackageSlash + nshort) != null - && !npackage.equals(currentPackagePoint)) // current package - || (context.getClass(nshort) != null); // default package + // check for another class which could 'shadow' this one. Three cases: + // 1) class with the same short name in the current package + // 2) class with the same short name in the default package + // 3) inner class with the same short name in the current class, a super class, or an implemented interface + boolean existsDefaultClass = + (context.getClass(currentPackageSlash + shortName) != null && !packageName.equals(currentPackagePoint)) || // current package + (context.getClass(shortName) != null && !currentPackagePoint.isEmpty()) || // default package + setInnerClassNames.contains(shortName); // inner class if (existsDefaultClass || - (mapSimpleNames.containsKey(nshort) && !npackage.equals(mapSimpleNames.get(nshort)))) { - return fullname; + (mapSimpleNames.containsKey(shortName) && !packageName.equals(mapSimpleNames.get(shortName)))) { + // don't return full name because if the class is a inner class, full name refers to the parent full name, not the child full name + return result == null ? fullName : (packageName + "." + result); } - else if (!mapSimpleNames.containsKey(nshort)) { - mapSimpleNames.put(nshort, npackage); - + else if (!mapSimpleNames.containsKey(shortName)) { + mapSimpleNames.put(shortName, packageName); if (!imported) { - setNotImportedNames.add(nshort); + setNotImportedNames.add(shortName); } } - return retname == null ? nshort : retname; + return result == null ? shortName : result; } - public int writeImports(TextBuffer buffer) { - - int importlines_written = 0; - String new_line_separator = DecompilerContext.getNewLineSeparator(); - + public void writeImports(TextBuffer buffer, boolean addSeparator) { List imports = packImports(); - - for (String s : imports) { - buffer.append("import "); - buffer.append(s); - buffer.append(";"); - buffer.append(new_line_separator); - - importlines_written++; + for (String line : imports) { + buffer.append("import ").append(line).append(';').appendLineSeparator(); + } + if (addSeparator && !imports.isEmpty()) { + buffer.appendLineSeparator(); } - - return importlines_written; } private List packImports() { - List> lst = new ArrayList>(mapSimpleNames.entrySet()); - - Collections.sort(lst, new Comparator>() { - public int compare(Entry par0, Entry par1) { - int res = par0.getValue().compareTo(par1.getValue()); - if (res == 0) { - res = par0.getKey().compareTo(par1.getKey()); - } - return res; - } - }); - - List res = new ArrayList(); - for (Entry ent : lst) { - // exclude a current class or one of the nested ones, java.lang and empty packages - if (!setNotImportedNames.contains(ent.getKey()) && - !JAVA_LANG_PACKAGE.equals(ent.getValue()) && - !ent.getValue().isEmpty() && !ent.getValue().equals(this.currentPackagePoint)) { // Spigot: Remove same package imports - res.add(ent.getValue() + "." + ent.getKey()); - } - } - - return res; + return mapSimpleNames.entrySet().stream() + .filter(ent -> + // exclude the current class or one of the nested ones + // empty, java.lang and the current packages + !setNotImportedNames.contains(ent.getKey()) && + !ent.getValue().isEmpty() && + !JAVA_LANG_PACKAGE.equals(ent.getValue()) && + !ent.getValue().equals(currentPackagePoint) + ) + .sorted(Map.Entry.comparingByValue().thenComparing(Map.Entry.comparingByKey())) + .map(ent -> ent.getValue() + "." + ent.getKey()) + .collect(Collectors.toList()); } } diff --git a/src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java b/src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java index 8d9d5c0..26378ce 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java @@ -1,29 +1,20 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.collectors; +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.struct.gen.VarType; + import java.util.Collection; import java.util.HashSet; +import java.util.Set; public class VarNamesCollector { - private HashSet usedNames = new HashSet(); + private final Set usedNames = new HashSet<>(); - public VarNamesCollector() { - } + public VarNamesCollector() { } public VarNamesCollector(Collection setNames) { usedNames.addAll(setNames); @@ -33,20 +24,140 @@ public class VarNamesCollector { usedNames.add(value); } - public String getFreeName(int index) { - return getFreeName("var" + index); - } - public String getFreeName(String proposition) { - - while (usedNames.contains(proposition)) { - proposition += "x"; + public static String removePrefix(String str, String prefix) + { + if (str.toLowerCase().startsWith(prefix)) + { + return str.substring(prefix.length()); + } + else + { + return str; } - usedNames.add(proposition); - return proposition; } - public HashSet getUsedNames() { - return usedNames; + public String getTypeLabel(VarType type, int index) { + return getTypeLabel(type, index, true); + } + + + public String getTypeLabel(VarType type, int index, boolean rand) { + String typeName = "tl_var" + index; + + // nasty hack + + + if (type == null) + { + + } + else if (type.type == CodeConstants.TYPE_OBJECT) + { + typeName = ExprProcessor.getTypeName(type, true); + typeName = removePrefix(typeName,"abstract"); + typeName = removePrefix(typeName,"client"); + + typeName = Character.toLowerCase(typeName.charAt(0)) + typeName.substring(1); + + switch (typeName) + { + case "class": + { + typeName = "clazz"; + break; + } + case "integer": + { + typeName = "i"; + break; + } + case "long": + { + typeName = "l"; + break; + } + case "int": + { + typeName = "i"; + break; + } + case "boolean": + { + typeName = "bool"; + break; + } + case "float": + { + typeName = "f"; + break; + } + case "double": + { + typeName = "d"; + break; + } + case "short": + { + typeName = "s"; + break; + } + case "byte": + { + typeName = "b"; + break; + } + case "enum": + { + typeName = "e"; + break; + } + + } + } + else if (type.type == CodeConstants.TYPE_NULL) + { + typeName = "null"; + } + else + { + typeName = String.valueOf(VarType.getTypeReverse(type)).toLowerCase(); + } + + if (typeName.contains(".")) + { + typeName = typeName.substring(0, typeName.indexOf('.')); + } + + if (typeName.equals("void")) + typeName = "v"; + + if (typeName.equals("enum")) + typeName = "e"; + + + + if (!rand) + { + return typeName; + } + + if (usedNames.contains(typeName)) + { + return getFreeName(typeName); + } + + addName(typeName); + return typeName; + } + public String getFreeName(String proposition) { + int i = 2; + String actual = proposition; + while (usedNames.contains(actual)) { + actual = proposition + i; + i++; + } + usedNames.add(actual); + return actual; } } diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java index 07b8578..7837939 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.decompiler; import org.jetbrains.java.decompiler.main.Fernflower; @@ -21,27 +7,30 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IResultSaver; import java.io.File; -import java.io.IOException; import java.util.Map; +@SuppressWarnings("unused") public class BaseDecompiler { - - private final Fernflower fernflower; + private final Fernflower engine; public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, Map options, IFernflowerLogger logger) { - fernflower = new Fernflower(provider, saver, options, logger); + engine = new Fernflower(provider, saver, options, logger); } - public void addSpace(File file, boolean isOwn) throws IOException { - fernflower.getStructContext().addSpace(file, isOwn); + public void addSource(File source) { + engine.addSource(source); + } + + public void addLibrary(File library) { + engine.addLibrary(library); } public void decompileContext() { try { - fernflower.decompileContext(); + engine.decompileContext(); } finally { - fernflower.clearContext(); + engine.clearContext(); } } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java index a486d9f..75d4d09 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.decompiler; import org.jetbrains.java.decompiler.main.DecompilerContext; @@ -23,6 +9,7 @@ import org.jetbrains.java.decompiler.main.extern.IResultSaver; import org.jetbrains.java.decompiler.util.InterpreterUtil; import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; @@ -31,7 +18,6 @@ import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { - @SuppressWarnings("UseOfSystemOutOrSystemErr") public static void main(String[] args) { if (args.length < 2) { @@ -41,21 +27,20 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { return; } - Map mapOptions = new HashMap(); - List lstSources = new ArrayList(); - List lstLibraries = new ArrayList(); + Map mapOptions = new HashMap<>(); + List sources = new ArrayList<>(); + List libraries = new ArrayList<>(); boolean isOption = true; for (int i = 0; i < args.length - 1; ++i) { // last parameter - destination String arg = args[i]; - if (isOption && arg.startsWith("-") && - arg.length() > 5 && arg.charAt(4) == '=') { - String value = arg.substring(5).toUpperCase(Locale.US); - if ("TRUE".equals(value)) { + if (isOption && arg.length() > 5 && arg.charAt(0) == '-' && arg.charAt(4) == '=') { + String value = arg.substring(5); + if ("true".equalsIgnoreCase(value)) { value = "1"; } - else if ("FALSE".equals(value)) { + else if ("false".equalsIgnoreCase(value)) { value = "0"; } @@ -65,15 +50,15 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { isOption = false; if (arg.startsWith("-e=")) { - lstLibraries.add(arg.substring(3)); + addPath(libraries, arg.substring(3)); } else { - lstSources.add(arg); + addPath(sources, arg); } } } - if (lstSources.isEmpty()) { + if (sources.isEmpty()) { System.out.println("error: no sources given"); return; } @@ -87,45 +72,55 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { PrintStreamLogger logger = new PrintStreamLogger(System.out); ConsoleDecompiler decompiler = new ConsoleDecompiler(destination, mapOptions, logger); - for (String source : lstSources) { - decompiler.addSpace(new File(source), true); + for (File library : libraries) { + decompiler.addLibrary(library); } - for (String library : lstLibraries) { - decompiler.addSpace(new File(library), false); + for (File source : sources) { + decompiler.addSource(source); } decompiler.decompileContext(); } + @SuppressWarnings("UseOfSystemOutOrSystemErr") + private static void addPath(List list, String path) { + File file = new File(path); + if (file.exists()) { + list.add(file); + } + else { + System.out.println("warn: missing '" + path + "', ignored"); + } + } + // ******************************************************************* // Implementation // ******************************************************************* private final File root; - private final Fernflower fernflower; - private Map mapArchiveStreams = new HashMap(); - private Map> mapArchiveEntries = new HashMap>(); - - @SuppressWarnings("UseOfSystemOutOrSystemErr") - public ConsoleDecompiler(File destination, Map options) { - this(destination, options, new PrintStreamLogger(System.out)); - } + private final Fernflower engine; + private final Map mapArchiveStreams = new HashMap<>(); + private final Map> mapArchiveEntries = new HashMap<>(); protected ConsoleDecompiler(File destination, Map options, IFernflowerLogger logger) { root = destination; - fernflower = new Fernflower(this, this, options, logger); + engine = new Fernflower(this, this, options, logger); } - public void addSpace(File file, boolean isOwn) { - fernflower.getStructContext().addSpace(file, isOwn); + public void addSource(File source) { + engine.addSource(source); + } + + public void addLibrary(File library) { + engine.addLibrary(library); } public void decompileContext() { try { - fernflower.decompileContext(); + engine.decompileContext(); } finally { - fernflower.clearContext(); + engine.clearContext(); } } @@ -140,17 +135,11 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { return InterpreterUtil.getBytes(file); } else { - ZipFile archive = new ZipFile(file); - try { + try (ZipFile archive = new ZipFile(file)) { ZipEntry entry = archive.getEntry(internalPath); - if (entry == null) { - throw new IOException("Entry not found: " + internalPath); - } + if (entry == null) throw new IOException("Entry not found: " + internalPath); return InterpreterUtil.getBytes(archive, entry); } - finally { - archive.close(); - } } } @@ -181,16 +170,10 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { } @Override - public void saveClassFile(String path, String qualifiedName, String entryName, String content) { + public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) { File file = new File(getAbsolutePath(path), entryName); - try { - Writer out = new OutputStreamWriter(new FileOutputStream(file), "UTF8"); - try { - out.write(content); - } - finally { - out.close(); - } + try (Writer out = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) { + out.write(content); } catch (IOException ex) { DecompilerContext.getLogger().writeMessage("Cannot write class file " + file, ex); @@ -206,7 +189,6 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { } FileOutputStream fileStream = new FileOutputStream(file); - @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") ZipOutputStream zipStream = manifest != null ? new JarOutputStream(fileStream, manifest) : new ZipOutputStream(fileStream); mapArchiveStreams.put(file.getPath(), zipStream); } @@ -228,21 +210,15 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { return; } - try { - ZipFile srcArchive = new ZipFile(new File(source)); - try { - ZipEntry entry = srcArchive.getEntry(entryName); - if (entry != null) { - InputStream in = srcArchive.getInputStream(entry); + try (ZipFile srcArchive = new ZipFile(new File(source))) { + ZipEntry entry = srcArchive.getEntry(entryName); + if (entry != null) { + try (InputStream in = srcArchive.getInputStream(entry)) { ZipOutputStream out = mapArchiveStreams.get(file); out.putNextEntry(new ZipEntry(entryName)); InterpreterUtil.copyStream(in, out); - in.close(); } } - finally { - srcArchive.close(); - } } catch (IOException ex) { String message = "Cannot copy entry " + entryName + " from " + source + " to " + file; @@ -262,7 +238,7 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { ZipOutputStream out = mapArchiveStreams.get(file); out.putNextEntry(new ZipEntry(entryName)); if (content != null) { - out.write(content.getBytes("UTF-8")); + out.write(content.getBytes(StandardCharsets.UTF_8)); } } catch (IOException ex) { @@ -272,10 +248,7 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { } private boolean checkEntry(String entryName, String file) { - Set set = mapArchiveEntries.get(file); - if (set == null) { - mapArchiveEntries.put(file, set = new HashSet()); - } + Set set = mapArchiveEntries.computeIfAbsent(file, k -> new HashSet<>()); boolean added = set.add(entryName); if (!added) { @@ -296,4 +269,4 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { DecompilerContext.getLogger().writeMessage("Cannot close " + file, IFernflowerLogger.Severity.WARN); } } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/PrintStreamLogger.java b/src/org/jetbrains/java/decompiler/main/decompiler/PrintStreamLogger.java index 27a2c56..ff3b9b3 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/PrintStreamLogger.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/PrintStreamLogger.java @@ -1,22 +1,8 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.decompiler; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; -import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextUtil; import java.io.PrintStream; @@ -33,62 +19,79 @@ public class PrintStreamLogger extends IFernflowerLogger { @Override public void writeMessage(String message, Severity severity) { if (accepts(severity)) { - stream.println(severity.prefix + InterpreterUtil.getIndentString(indent) + message); + stream.println(severity.prefix + TextUtil.getIndentString(indent) + message); } } @Override - public void writeMessage(String message, Throwable t) { - writeMessage(message, Severity.ERROR); - if (accepts(Severity.ERROR)) { + public void writeMessage(String message, Severity severity, Throwable t) { + if (accepts(severity)) { + writeMessage(message, severity); t.printStackTrace(stream); } } @Override public void startReadingClass(String className) { - writeMessage("Decompiling class " + className, Severity.INFO); - ++indent; + if (accepts(Severity.INFO)) { + writeMessage("Decompiling class " + className, Severity.INFO); + ++indent; + } } @Override public void endReadingClass() { - --indent; - writeMessage("... done", Severity.INFO); + if (accepts(Severity.INFO)) { + --indent; + writeMessage("... done", Severity.INFO); + } } @Override public void startClass(String className) { - writeMessage("Processing class " + className, Severity.TRACE); - ++indent; + if (accepts(Severity.INFO)) { + writeMessage("Processing class " + className, Severity.TRACE); + ++indent; + } } @Override public void endClass() { - --indent; - writeMessage("... proceeded", Severity.TRACE); + if (accepts(Severity.INFO)) { + --indent; + writeMessage("... proceeded", Severity.TRACE); + } } @Override public void startMethod(String methodName) { - writeMessage("Processing method " + methodName, Severity.TRACE); - ++indent; + if (accepts(Severity.INFO)) { + writeMessage("Processing method " + methodName, Severity.TRACE); + ++indent; + } } + @Override public void endMethod() { - --indent; - writeMessage("... proceeded", Severity.TRACE); + if (accepts(Severity.INFO)) { + --indent; + writeMessage("... proceeded", Severity.TRACE); + } } @Override public void startWriteClass(String className) { - writeMessage("Writing class " + className, Severity.TRACE); - ++indent; + if (accepts(Severity.INFO)) { + writeMessage("Writing class " + className, Severity.TRACE); + ++indent; + } } @Override public void endWriteClass() { - --indent; - writeMessage("... written", Severity.TRACE); + if (accepts(Severity.INFO)) { + --indent; + writeMessage("... written", Severity.TRACE); + } } } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java b/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java index adf3270..ac72d7c 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.extern; import java.io.IOException; diff --git a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java index db76c77..c9ece03 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.extern; public abstract class IFernflowerLogger { @@ -39,7 +25,11 @@ public abstract class IFernflowerLogger { public abstract void writeMessage(String message, Severity severity); - public abstract void writeMessage(String message, Throwable t); + public abstract void writeMessage(String message, Severity severity, Throwable t); + + public void writeMessage(String message, Throwable t) { + writeMessage(message, Severity.ERROR, t); + } public void startReadingClass(String className) { } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java index 7d96e62..5448e79 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.extern; import org.jetbrains.java.decompiler.util.InterpreterUtil; @@ -31,6 +17,7 @@ public interface IFernflowerPreferences { String HIDE_DEFAULT_CONSTRUCTOR = "hdc"; String DECOMPILE_GENERIC_SIGNATURES = "dgs"; String NO_EXCEPTIONS_RETURN = "ner"; + String ENSURE_SYNCHRONIZED_MONITOR = "esm"; String DECOMPILE_ENUM = "den"; String REMOVE_GET_CLASS_NEW = "rgn"; String LITERALS_AS_IS = "lit"; @@ -39,13 +26,14 @@ public interface IFernflowerPreferences { String SYNTHETIC_NOT_SET = "nns"; String UNDEFINED_PARAM_TYPE_OBJECT = "uto"; String USE_DEBUG_VAR_NAMES = "udv"; + String USE_METHOD_PARAMETERS = "ump"; String REMOVE_EMPTY_RANGES = "rer"; String FINALLY_DEINLINE = "fdi"; String IDEA_NOT_NULL_ANNOTATION = "inn"; String LAMBDA_TO_ANONYMOUS_CLASS = "lac"; - String BYTECODE_SOURCE_MAPPING = "bsm"; - String USE_DEBUG_LINE_NUMBERS = "udl"; + String IGNORE_INVALID_BYTECODE = "iib"; + String VERIFY_ANONYMOUS_CLASSES = "vac"; String LOG_LEVEL = "log"; String MAX_PROCESSING_METHOD = "mpm"; @@ -53,44 +41,55 @@ public interface IFernflowerPreferences { String USER_RENAMER_CLASS = "urc"; String NEW_LINE_SEPARATOR = "nls"; String INDENT_STRING = "ind"; - String BANNER = "ban"; + String DUMP_ORIGINAL_LINES = "__dump_original_lines__"; + String UNIT_TEST_MODE = "__unit_test_mode__"; + String LINE_SEPARATOR_WIN = "\r\n"; - String LINE_SEPARATOR_LIN = "\n"; + String LINE_SEPARATOR_UNX = "\n"; - Map DEFAULTS = Collections.unmodifiableMap(new HashMap() {{ - put(REMOVE_BRIDGE, "1"); - put(REMOVE_SYNTHETIC, "0"); - put(DECOMPILE_INNER, "1"); - put(DECOMPILE_CLASS_1_4, "1"); - put(DECOMPILE_ASSERTIONS, "1"); - put(HIDE_EMPTY_SUPER, "1"); - put(HIDE_DEFAULT_CONSTRUCTOR, "1"); - put(DECOMPILE_GENERIC_SIGNATURES, "0"); - put(NO_EXCEPTIONS_RETURN, "1"); - put(DECOMPILE_ENUM, "1"); - put(REMOVE_GET_CLASS_NEW, "1"); - put(LITERALS_AS_IS, "0"); - put(BOOLEAN_TRUE_ONE, "1"); - put(ASCII_STRING_CHARACTERS, "0"); - put(SYNTHETIC_NOT_SET, "1"); - put(UNDEFINED_PARAM_TYPE_OBJECT, "1"); - put(USE_DEBUG_VAR_NAMES, "1"); - put(REMOVE_EMPTY_RANGES, "1"); - put(FINALLY_DEINLINE, "1"); - put(IDEA_NOT_NULL_ANNOTATION, "1"); - put(LAMBDA_TO_ANONYMOUS_CLASS, "0"); + Map DEFAULTS = getDefaults(); - put(BYTECODE_SOURCE_MAPPING, "0"); - put(USE_DEBUG_LINE_NUMBERS, "0"); + static Map getDefaults() { + Map defaults = new HashMap<>(); - put(BANNER, ""); + defaults.put(REMOVE_BRIDGE, "1"); + defaults.put(REMOVE_SYNTHETIC, "0"); + defaults.put(DECOMPILE_INNER, "1"); + defaults.put(DECOMPILE_CLASS_1_4, "1"); + defaults.put(DECOMPILE_ASSERTIONS, "1"); + defaults.put(HIDE_EMPTY_SUPER, "1"); + defaults.put(HIDE_DEFAULT_CONSTRUCTOR, "1"); + defaults.put(DECOMPILE_GENERIC_SIGNATURES, "0"); + defaults.put(NO_EXCEPTIONS_RETURN, "1"); + defaults.put(ENSURE_SYNCHRONIZED_MONITOR, "1"); + defaults.put(DECOMPILE_ENUM, "1"); + defaults.put(REMOVE_GET_CLASS_NEW, "1"); + defaults.put(LITERALS_AS_IS, "0"); + defaults.put(BOOLEAN_TRUE_ONE, "1"); + defaults.put(ASCII_STRING_CHARACTERS, "0"); + defaults.put(SYNTHETIC_NOT_SET, "0"); + defaults.put(UNDEFINED_PARAM_TYPE_OBJECT, "1"); + defaults.put(USE_DEBUG_VAR_NAMES, "1"); + defaults.put(USE_METHOD_PARAMETERS, "1"); + defaults.put(REMOVE_EMPTY_RANGES, "1"); + defaults.put(FINALLY_DEINLINE, "1"); + defaults.put(IDEA_NOT_NULL_ANNOTATION, "1"); + defaults.put(LAMBDA_TO_ANONYMOUS_CLASS, "0"); + defaults.put(BYTECODE_SOURCE_MAPPING, "0"); + defaults.put(IGNORE_INVALID_BYTECODE, "0"); + defaults.put(VERIFY_ANONYMOUS_CLASSES, "0"); - put(LOG_LEVEL, IFernflowerLogger.Severity.INFO.name()); - put(MAX_PROCESSING_METHOD, "0"); - put(RENAME_ENTITIES, "0"); - put(NEW_LINE_SEPARATOR, (InterpreterUtil.IS_WINDOWS ? "0" : "1")); - put(INDENT_STRING, " "); - }}); -} + defaults.put(LOG_LEVEL, IFernflowerLogger.Severity.INFO.name()); + defaults.put(MAX_PROCESSING_METHOD, "0"); + defaults.put(RENAME_ENTITIES, "0"); + defaults.put(NEW_LINE_SEPARATOR, (InterpreterUtil.IS_WINDOWS ? "0" : "1")); + defaults.put(INDENT_STRING, " "); + defaults.put(BANNER, ""); + defaults.put(UNIT_TEST_MODE, "0"); + defaults.put(DUMP_ORIGINAL_LINES, "0"); + + return Collections.unmodifiableMap(defaults); + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java b/src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java index fc0971e..59d7be7 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java @@ -1,35 +1,15 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.extern; - public interface IIdentifierRenamer { - int ELEMENT_CLASS = 1; + enum Type {ELEMENT_CLASS, ELEMENT_FIELD, ELEMENT_METHOD} - int ELEMENT_FIELD = 2; + boolean toBeRenamed(Type elementType, String className, String element, String descriptor); - int ELEMENT_METHOD = 3; + String getNextClassName(String fullName, String shortName); + String getNextFieldName(String className, String field, String descriptor); - boolean toBeRenamed(int element_type, String classname, String element, String descriptor); - - String getNextClassname(String fullname, String shortname); - - String getNextFieldname(String classname, String field, String descriptor); - - String getNextMethodname(String classname, String method, String descriptor); + String getNextMethodName(String className, String method, String descriptor); } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java b/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java index 1e6a7d4..fad1ddc 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.extern; import java.util.jar.Manifest; @@ -22,7 +8,7 @@ public interface IResultSaver { void copyFile(String source, String path, String entryName); - void saveClassFile(String path, String qualifiedName, String entryName, String content); + void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping); void createArchive(String path, String archiveName, Manifest manifest); diff --git a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java index b89bcd2..eda4006 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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,64 +8,49 @@ import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.StructClass; -import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.StructMethod; -import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; -import java.io.IOException; import java.util.HashSet; +import java.util.List; import java.util.Set; public class ClassWrapper { - - private StructClass classStruct; - private Set hiddenMembers = new HashSet(); - private VBStyleCollection staticFieldInitializers = new VBStyleCollection(); - private VBStyleCollection dynamicFieldInitializers = new VBStyleCollection(); - private VBStyleCollection methods = new VBStyleCollection(); - + private final StructClass classStruct; + private final Set hiddenMembers = new HashSet<>(); + private final VBStyleCollection staticFieldInitializers = new VBStyleCollection<>(); + private final VBStyleCollection dynamicFieldInitializers = new VBStyleCollection<>(); + private final VBStyleCollection methods = new VBStyleCollection<>(); public ClassWrapper(StructClass classStruct) { this.classStruct = classStruct; } - public void init() throws IOException { - + public void init() { DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct); - + DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_WRAPPER, this); DecompilerContext.getLogger().startClass(classStruct.qualifiedName); - // collect field names - HashSet setFieldNames = new HashSet(); - for (StructField fd : classStruct.getFields()) { - setFieldNames.add(fd.getName()); - } - - int maxsec = Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString()); + int maxSec = Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString()); + boolean testMode = DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE); for (StructMethod mt : classStruct.getMethods()) { - DecompilerContext.getLogger().startMethod(mt.getName() + " " + mt.getDescriptor()); - VarNamesCollector vc = new VarNamesCollector(); - DecompilerContext.setVarNamesCollector(vc); + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + VarProcessor varProc = new VarProcessor(mt, md); + DecompilerContext.startMethod(varProc); - CounterContainer counter = new CounterContainer(); - DecompilerContext.setCounterContainer(counter); - - DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD, mt); - DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR, MethodDescriptor.parseDescriptor(mt.getDescriptor())); - - VarProcessor varproc = new VarProcessor(); - DecompilerContext.setProperty(DecompilerContext.CURRENT_VAR_PROCESSOR, varproc); + VarNamesCollector vc = varProc.getVarNamesCollector(); + CounterContainer counter = DecompilerContext.getCounterContainer(); RootStatement root = null; @@ -87,86 +58,113 @@ public class ClassWrapper { try { if (mt.containsCode()) { - - if (maxsec == 0) { // blocking wait - root = MethodProcessorThread.codeToJava(mt, varproc); + if (maxSec == 0 || testMode) { + root = MethodProcessorRunnable.codeToJava(classStruct, mt, md, varProc); } else { - MethodProcessorThread mtproc = new MethodProcessorThread(mt, varproc, DecompilerContext.getCurrentContext()); - Thread mtthread = new Thread(mtproc); - long stopAt = System.currentTimeMillis() + maxsec * 1000; + MethodProcessorRunnable mtProc = new MethodProcessorRunnable(classStruct, mt, md, varProc, DecompilerContext.getCurrentContext()); - mtthread.start(); + Thread mtThread = new Thread(mtProc, "Java decompiler"); + long stopAt = System.currentTimeMillis() + maxSec * 1000L; - while (mtthread.isAlive()) { + mtThread.start(); - synchronized (mtproc.lock) { - mtproc.lock.wait(100); + while (!mtProc.isFinished()) { + try { + synchronized (mtProc.lock) { + mtProc.lock.wait(200); + } + } + catch (InterruptedException e) { + killThread(mtThread); + throw e; } if (System.currentTimeMillis() >= stopAt) { String message = "Processing time limit exceeded for method " + mt.getName() + ", execution interrupted."; DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.ERROR); - killThread(mtthread); + killThread(mtThread); isError = true; break; } } if (!isError) { - root = mtproc.getResult(); + root = mtProc.getResult(); } } } else { - boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC); - MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC); - int paramcount = 0; - if (thisvar) { - varproc.getThisvars().put(new VarVersionPaar(0, 0), classStruct.qualifiedName); - paramcount = 1; + int paramCount = 0; + if (thisVar) { + varProc.getThisVars().put(new VarVersionPair(0, 0), classStruct.qualifiedName); + paramCount = 1; } - paramcount += md.params.length; + paramCount += md.params.length; - int varindex = 0; - for (int i = 0; i < paramcount; i++) { - varproc.setVarName(new VarVersionPaar(varindex, 0, classStruct.qualifiedName, false), vc.getFreeName(varindex)); + int varIndex = 0; + for (int i = 0; i < paramCount; i++) { + varProc.setVarName(new VarVersionPair(varIndex, 0), "tbd");//, vc.getFreeName(varIndex)); - if (thisvar) { + if (thisVar) { if (i == 0) { - varindex++; + varIndex++; } else { - varindex += md.params[i - 1].stack_size; + varProc.setVarName(new VarVersionPair(varIndex, 0), vc.getTypeLabel(md.params[i - 1], varIndex)); + varIndex += md.params[i - 1].stackSize; } } else { - varindex += md.params[i].stack_size; + varProc.setVarName(new VarVersionPair(varIndex, 0), vc.getTypeLabel(md.params[i], varIndex)); + varIndex += md.params[i].stackSize; } } } } - catch (Throwable ex) { - DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled.", ex); + catch (Throwable t) { + String message = "Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled."; + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t); isError = true; } - MethodWrapper meth = new MethodWrapper(root, varproc, mt, counter); - meth.decompiledWithErrors = isError; + MethodWrapper methodWrapper = new MethodWrapper(root, varProc, mt, counter); + methodWrapper.decompiledWithErrors = isError; - methods.addWithKey(meth, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor())); + methods.addWithKey(methodWrapper, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor())); - // rename vars so that no one has the same name as a field - varproc.refreshVarNames(new VarNamesCollector(setFieldNames)); + if (!isError) { + // rename vars so that no one has the same name as a field + VarNamesCollector namesCollector = new VarNamesCollector(); + // well, actually, no -reece + //classStruct.getFields().forEach(f -> namesCollector.addName(f.getName())); + varProc.refreshVarNames(namesCollector); - // if debug information present and should be used - if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) { - StructLocalVariableTableAttribute attr = (StructLocalVariableTableAttribute)mt.getAttributes().getWithKey( - StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE); + // if debug information present and should be used + if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) { + StructLocalVariableTableAttribute attr = mt.getLocalVariableAttr(); + if (attr != null) { + // only param names here + varProc.setDebugVarNames(attr.getMapParamNames()); - if (attr != null) { - varproc.setDebugVarNames(attr.getMapVarNames()); + // the rest is here + methodWrapper.getOrBuildGraph().iterateExprents(exprent -> { + List lst = exprent.getAllExprents(true); + lst.add(exprent); + lst.stream() + .filter(e -> e.type == Exprent.EXPRENT_VAR) + .forEach(e -> { + VarExprent varExprent = (VarExprent)e; + String name = varExprent.getDebugName(mt); + if (name != null) { + varProc.setVarName(varExprent.getVarVersionPair(), name); + } + }); + return 0; + }); + } } } @@ -204,4 +202,9 @@ public class ClassWrapper { public VBStyleCollection getDynamicFieldInitializers() { return dynamicFieldInitializers; } -} + + @Override + public String toString() { + return classStruct.qualifiedName; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java index 437568b..2ad5b20 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -35,36 +21,28 @@ import java.io.IOException; import java.util.*; public class LambdaProcessor { - @SuppressWarnings("SpellCheckingInspection") private static final String JAVAC_LAMBDA_CLASS = "java/lang/invoke/LambdaMetafactory"; @SuppressWarnings("SpellCheckingInspection") private static final String JAVAC_LAMBDA_METHOD = "metafactory"; @SuppressWarnings("SpellCheckingInspection") private static final String JAVAC_LAMBDA_ALT_METHOD = "altMetafactory"; public void processClass(ClassNode node) throws IOException { - for (ClassNode child : node.nested) { processClass(child); } - hasLambda(node); - } - - public boolean hasLambda(ClassNode node) throws IOException { - - ClassesProcessor clprocessor = DecompilerContext.getClassProcessor(); + ClassesProcessor clProcessor = DecompilerContext.getClassProcessor(); StructClass cl = node.classStruct; - if (cl.getBytecodeVersion() < CodeConstants.BYTECODE_JAVA_8) { // lamda beginning with Java 8 - return false; + if (!cl.isVersion8()) { // lambda beginning with Java 8 + return; } - StructBootstrapMethodsAttribute bootstrap = - (StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); + StructBootstrapMethodsAttribute bootstrap = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); if (bootstrap == null || bootstrap.getMethodsNumber() == 0) { - return false; // no bootstrap constants in pool + return; // no bootstrap constants in pool } - BitSet lambda_methods = new BitSet(); + BitSet lambdaMethods = new BitSet(); // find lambda bootstrap constants for (int i = 0; i < bootstrap.getMethodsNumber(); ++i) { @@ -73,19 +51,19 @@ 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()) { - return false; // no lambda bootstrap constant found + if (lambdaMethods.isEmpty()) { + return; // no lambda bootstrap constant found } - Map mapMethodsLambda = new HashMap(); + Map mapMethodsLambda = new HashMap<>(); // 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) { @@ -95,9 +73,9 @@ public class LambdaProcessor { Instruction instr = seq.getInstr(i); if (instr.opcode == CodeConstants.opc_invokedynamic) { - LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.getOperand(0)); + LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.operand(0)); - if (lambda_methods.get(invoke_dynamic.index1)) { // lambda invocation found + if (lambdaMethods.get(invoke_dynamic.index1)) { // lambda invocation found List bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1); MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor); @@ -117,9 +95,9 @@ public class LambdaProcessor { node.nested.add(node_lambda); node_lambda.parent = node; - clprocessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda); - if (!node_lambda.lambda_information.is_method_reference) { - mapMethodsLambda.put(node_lambda.lambda_information.content_method_key, node_lambda.simpleName); + clProcessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda); + if (!node_lambda.lambdaInformation.is_method_reference) { + mapMethodsLambda.put(node_lambda.lambdaInformation.content_method_key, node_lambda.simpleName); } } } @@ -134,7 +112,7 @@ public class LambdaProcessor { if (nd.type == ClassNode.CLASS_LAMBDA) { String parent_class_name = mapMethodsLambda.get(nd.enclosingMethod); if (parent_class_name != null) { - ClassNode parent_class = clprocessor.getMapRootClasses().get(parent_class_name); + ClassNode parent_class = clProcessor.getMapRootClasses().get(parent_class_name); parent_class.nested.add(nd); nd.parent = parent_class; @@ -143,7 +121,5 @@ public class LambdaProcessor { } // FIXME: mixed hierarchy? - - return false; } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java similarity index 51% rename from src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java rename to src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index 2f98cf8..d9de3d5 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -1,20 +1,7 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; import org.jetbrains.java.decompiler.code.InstructionSequence; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.main.DecompilerContext; @@ -28,81 +15,73 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import java.io.IOException; -public class MethodProcessorThread implements Runnable { - +public class MethodProcessorRunnable implements Runnable { public final Object lock = new Object(); + private final StructClass klass; private final StructMethod method; - private final VarProcessor varproc; + private final MethodDescriptor methodDescriptor; + private final VarProcessor varProc; private final DecompilerContext parentContext; private volatile RootStatement root; private volatile Throwable error; + private volatile boolean finished = false; - public MethodProcessorThread(StructMethod method, VarProcessor varproc, DecompilerContext parentContext) { + public MethodProcessorRunnable(StructClass klass, + StructMethod method, + MethodDescriptor methodDescriptor, + VarProcessor varProc, + DecompilerContext parentContext) { + this.klass = klass; this.method = method; - this.varproc = varproc; + this.methodDescriptor = methodDescriptor; + this.varProc = varProc; this.parentContext = parentContext; } + @Override public void run() { - - DecompilerContext.setCurrentContext(parentContext); - error = null; root = null; try { - root = codeToJava(method, varproc); + DecompilerContext.setCurrentContext(parentContext); + root = codeToJava(klass, method, methodDescriptor, varProc); + } + catch (Throwable t) { + error = t; + } + finally { + DecompilerContext.setCurrentContext(null); + } - synchronized (lock) { - lock.notifyAll(); - } - } - catch (ThreadDeath ex) { - throw ex; - } - catch (Throwable ex) { - error = ex; + finished = true; + synchronized (lock) { + lock.notifyAll(); } } - public static RootStatement codeToJava(StructMethod mt, VarProcessor varproc) throws IOException { + 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 - StructClass cl = mt.getClassStruct(); - - boolean isInitializer = "".equals(mt.getName()); // for now static initializer only - - mt.expandData(); + mt.expandData(cl); InstructionSequence seq = mt.getInstructionSequence(); ControlFlowGraph graph = new ControlFlowGraph(seq); - // System.out.println(graph.toString()); - - - // if(mt.getName().endsWith("_getActiveServers")) { - // System.out.println(); - // } - - //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern1.dot"), true); - DeadCodeHelper.removeDeadBlocks(graph); - graph.inlineJsr(mt); - - // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern4.dot"), true); + graph.inlineJsr(cl, mt); // TODO: move to the start, before jsr inlining DeadCodeHelper.connectDummyExitBlock(graph); DeadCodeHelper.removeGotos(graph); + ExceptionDeobfuscator.removeCircularRanges(graph); - //DeadCodeHelper.removeCircularRanges(graph); - - - // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); ExceptionDeobfuscator.restorePopRanges(graph); @@ -110,15 +89,16 @@ public class MethodProcessorThread implements Runnable { ExceptionDeobfuscator.removeEmptyRanges(graph); } - // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); + if (DecompilerContext.getOption(IFernflowerPreferences.ENSURE_SYNCHRONIZED_MONITOR)) { + // special case: search for 'synchronized' ranges w/o monitorexit instruction (as generated by Kotlin and Scala) + DeadCodeHelper.extendSynchronizedRangeToMonitorexit(graph); + } if (DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) { // special case: single return instruction outside of a protected range DeadCodeHelper.incorporateValueReturns(graph); } - // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true); - // ExceptionDeobfuscator.restorePopRanges(graph); ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph); @@ -126,22 +106,18 @@ public class MethodProcessorThread implements Runnable { DecompilerContext.getCounterContainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables()); - //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); - //System.out.println(graph.toString()); - if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) { DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.Severity.WARN); + if (!ExceptionDeobfuscator.handleMultipleEntryExceptionRanges(graph)) { + DecompilerContext.getLogger().writeMessage("Found multiple entry exception ranges which could not be splitted", IFernflowerLogger.Severity.WARN); + } + ExceptionDeobfuscator.insertDummyExceptionHandlerBlocks(graph, mt.getBytecodeVersion()); } RootStatement root = DomHelper.parseGraph(graph); - FinallyProcessor fproc = new FinallyProcessor(varproc); - while (fproc.iterateGraph(mt, root, graph)) { - - //DotExporter.toDotFile(graph, new File("c:\\Temp\\fern2.dot"), true); - //System.out.println(graph.toString()); - //System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - + FinallyProcessor fProc = new FinallyProcessor(md, varProc); + while (fProc.iterateGraph(cl, mt, root, graph)) { root = DomHelper.parseGraph(graph); } @@ -149,72 +125,43 @@ public class MethodProcessorThread implements Runnable { // not until now because of comparison between synchronized statements in the finally cycle DomHelper.removeSynchronizedHandler(root); - // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); - // System.out.println(graph.toString()); - // LabelHelper.lowContinueLabels(root, new HashSet()); SequenceHelper.condenseSequences(root); ClearStructHelper.clearStatements(root); - ExprProcessor proc = new ExprProcessor(); + ExprProcessor proc = new ExprProcessor(md, varProc); proc.processStatement(root, cl); - // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern3.dot"), true); - // System.out.println(graph.toString()); + SequenceHelper.condenseSequences(root); - //System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + StackVarsProcessor stackProc = new StackVarsProcessor(); - while (true) { - StackVarsProcessor stackproc = new StackVarsProcessor(); - stackproc.simplifyStackVars(root, mt, cl); - - //System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - - varproc.setVarVersions(root); - - // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - - if (!new PPandMMHelper().findPPandMM(root)) { - break; - } + do { + stackProc.simplifyStackVars(root, mt, cl); + varProc.setVarVersions(root); } + while (new PPandMMHelper().findPPandMM(root)); while (true) { - LabelHelper.cleanUpEdges(root); - while (true) { - + do { MergeHelper.enhanceLoops(root); - - if (LoopExtractHelper.extractLoops(root)) { - continue; - } - - if (!IfHelper.mergeAllIfs(root)) { - break; - } } + while (LoopExtractHelper.extractLoops(root) || IfHelper.mergeAllIfs(root)); if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) { - if (IdeaNotNullHelper.removeHardcodedChecks(root, mt)) { - SequenceHelper.condenseSequences(root); - - StackVarsProcessor stackproc = new StackVarsProcessor(); - stackproc.simplifyStackVars(root, mt, cl); - - varproc.setVarVersions(root); + stackProc.simplifyStackVars(root, mt, cl); + varProc.setVarVersions(root); } } LabelHelper.identifyLabels(root); - // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - if (InlineSingleBlockHelper.inlineSingleBlocks(root)) { continue; } @@ -225,16 +172,16 @@ public class MethodProcessorThread implements Runnable { } // FIXME: !! - // if(!EliminateLoopsHelper.eliminateLoops(root)) { - // break; - // } + //if(!EliminateLoopsHelper.eliminateLoops(root)) { + // break; + //} } ExitHelper.removeRedundantReturns(root); - SecondaryFunctionsHelper.identifySecondaryFunctions(root); + SecondaryFunctionsHelper.identifySecondaryFunctions(root, varProc); - varproc.setVarDefinitions(root); + varProc.setVarDefinitions(root); // must be the last invocation, because it makes the statement structure inconsistent // FIXME: new edge type needed @@ -242,8 +189,6 @@ public class MethodProcessorThread implements Runnable { mt.releaseResources(); - // System.out.println("++++++++++++++++++++++/// \r\n"+root.toJava()); - return root; } @@ -253,7 +198,7 @@ public class MethodProcessorThread implements Runnable { return root; } - public Throwable getError() { - return error; + public boolean isFinished() { + return finished; } } diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java index d36b8bc..51a768d 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.rels; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; @@ -20,31 +6,24 @@ import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.StructMethod; import java.util.HashSet; import java.util.List; - +import java.util.Set; public class MethodWrapper { - - public RootStatement root; - - public VarProcessor varproc; - - public StructMethod methodStruct; - - public CounterContainer counter; + public final RootStatement root; + public final VarProcessor varproc; + public final StructMethod methodStruct; + public final CounterContainer counter; + public final Set setOuterVarNames = new HashSet<>(); public DirectGraph graph; - - public List signatureFields; - + public List synthParameters; public boolean decompiledWithErrors; - public HashSet setOuterVarNames = new HashSet(); - public MethodWrapper(RootStatement root, VarProcessor varproc, StructMethod methodStruct, CounterContainer counter) { this.root = root; this.varproc = varproc; @@ -54,9 +33,13 @@ public class MethodWrapper { public DirectGraph getOrBuildGraph() { if (graph == null && root != null) { - FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); - graph = flatthelper.buildDirectGraph(root); + graph = new FlattenStatementsHelper().buildDirectGraph(root); } return graph; } -} + + @Override + public String toString() { + return methodStruct.getName(); + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java index c4a4ff5..da17532 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java @@ -1,22 +1,7 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.rels; import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; @@ -26,36 +11,30 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode; -import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.attr.StructEnclosingMethodAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; import java.util.*; import java.util.Map.Entry; -import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; public class NestedClassProcessor { - - public void processClass(ClassNode root, ClassNode node) { - // hide synthetic lambda content methods - if (node.type == ClassNode.CLASS_LAMBDA && !node.lambda_information.is_method_reference) { + if (node.type == ClassNode.CLASS_LAMBDA && !node.lambdaInformation.is_method_reference) { ClassNode node_content = DecompilerContext.getClassProcessor().getMapRootClasses().get(node.classStruct.qualifiedName); - if (node_content != null && node_content.wrapper != null) { - node_content.wrapper.getHiddenMembers().add(node.lambda_information.content_method_key); + if (node_content != null && node_content.getWrapper() != null) { + node_content.getWrapper().getHiddenMembers().add(node.lambdaInformation.content_method_key); } } @@ -63,12 +42,7 @@ public class NestedClassProcessor { return; } - if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM)) { - gatherEnumSwitchMaps(node); - } - if (node.type != ClassNode.CLASS_LAMBDA) { - computeLocalVarsAndDefinitions(node); // for each local or anonymous class ensure not empty enclosing method @@ -77,15 +51,15 @@ public class NestedClassProcessor { int nameless = 0, synthetics = 0; for (ClassNode child : node.nested) { + StructClass cl = child.classStruct; // ensure not-empty class name if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_MEMBER) && child.simpleName == null) { - StructClass cl = child.classStruct; if ((child.access & CodeConstants.ACC_SYNTHETIC) != 0 || cl.isSynthetic()) { child.simpleName = "SyntheticClass_" + (++synthetics); } else { - DecompilerContext.getLogger().writeMessage("Nameless local or member class " + cl.qualifiedName + "!", - IFernflowerLogger.Severity.WARN); + String message = "Nameless local or member class " + cl.qualifiedName + "!"; + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); child.simpleName = "NamelessClass_" + (++nameless); } } @@ -95,12 +69,13 @@ public class NestedClassProcessor { if (child.type == ClassNode.CLASS_LAMBDA) { setLambdaVars(node, child); } - else { - if (child.type != ClassNode.CLASS_MEMBER || (child.access & CodeConstants.ACC_STATIC) == 0) { - insertLocalVars(node, child); + else if (child.type != ClassNode.CLASS_MEMBER || (child.access & CodeConstants.ACC_STATIC) == 0) { + insertLocalVars(node, child); - if (child.type == ClassNode.CLASS_LOCAL) { - setLocalClassDefinition(node.wrapper.getMethods().getWithKey(child.enclosingMethod), child); + if (child.type == ClassNode.CLASS_LOCAL && child.enclosingMethod != null) { + MethodWrapper enclosingMethodWrapper = node.getWrapper().getMethods().getWithKey(child.enclosingMethod); + if(enclosingMethodWrapper != null) { // e.g. in case of switch-on-enum. FIXME: some proper handling of multiple enclosing classes + setLocalClassDefinition(enclosingMethodWrapper, child); } } } @@ -111,234 +86,102 @@ public class NestedClassProcessor { } } - /** - * When Java introduced Enums they aded the ability to use them in Switch statements. - * This was done in a purely syntax sugar way using the old switch on int methods. - * The compiler creates a synthetic class with a static int array field. - * To support enums changing post compile, It initializes this field with a length of the current enum length. - * And then for every referenced enum value it adds a mapping in the form of: - * try { - * field[Enum.VALUE.ordinal()] = 1; - * } catch (FieldNotFoundException e) {} - * - * If a class has multiple switches on multiple enums, the compiler adds the init and try list to the BEGINNING of the static initalizer. - * But they add the field to the END of the fields list. - */ - private void gatherEnumSwitchMaps(ClassNode node) { - if (node.wrapper == null) { - return; - } - for (ClassNode child : node.nested) { - gatherEnumSwitchMaps(child); - } - - MethodWrapper clinit = node.wrapper.getMethodWrapper("", "()V"); - if (clinit == null || clinit.root == null || clinit.root.getFirst().type != Statement.TYPE_SEQUENCE) { - return; - } - - final int STATIC_FINAL_SYNTHETIC = CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL | CodeConstants.ACC_SYNTHETIC; - Set potentialFields = new HashSet(); - for (StructField fd : node.classStruct.getFields()) { - if ((fd.getAccessFlags() & STATIC_FINAL_SYNTHETIC) == STATIC_FINAL_SYNTHETIC && "[I".equals(fd.getDescriptor())) { - potentialFields.add(fd.getName()); - } - } - - if (potentialFields.size() == 0) { - return; - } - - SequenceStatement seq = (SequenceStatement)clinit.root.getFirst(); - for (int x = 0; x < seq.getStats().size();) { - Statement stat = seq.getStats().get(x); - if (stat.type != Statement.TYPE_BASICBLOCK || stat.getExprents() == null || stat.getExprents().size() != 1 || stat.getExprents().get(0).type != Exprent.EXPRENT_ASSIGNMENT) { - break; - } - AssignmentExprent ass = (AssignmentExprent)stat.getExprents().get(0); - if (ass.getLeft().type != Exprent.EXPRENT_FIELD || ass.getRight().type != Exprent.EXPRENT_NEW) { - break; - } - FieldExprent mapField = (FieldExprent)ass.getLeft(); - NewExprent _new = ((NewExprent)ass.getRight()); - if (!mapField.getClassname().equals(node.classStruct.qualifiedName) || !potentialFields.contains(mapField.getName()) || - _new.getNewtype().type != CodeConstants.TYPE_INT || _new.getNewtype().arraydim != 1 || - _new.getLstDims().size() != 1 || _new.getLstDims().get(0).type != Exprent.EXPRENT_FUNCTION) { - break; - } - FunctionExprent func = (FunctionExprent)_new.getLstDims().get(0); - if (func.getFunctype() != FunctionExprent.FUNCTION_ARRAYLENGTH || func.getLstOperands().size() != 1 || func.getLstOperands().get(0).type != Exprent.EXPRENT_INVOCATION) { - break; - } - InvocationExprent invoc = (InvocationExprent)func.getLstOperands().get(0); - if (!"values".equals(invoc.getName()) || !("()[L" + invoc.getClassname() + ";").equals(invoc.getStringDescriptor())) { - break; - } - - String fieldName = mapField.getName(); - String enumName = invoc.getClassname(); - Map idToName = new HashMap(); - - boolean replace = false; - int y = x; - while (++y < seq.getStats().size()) { - if (seq.getStats().get(y).type != Statement.TYPE_TRYCATCH) { - break; - } - CatchStatement _try = (CatchStatement)seq.getStats().get(y); - Statement first = _try.getFirst(); - List exprents = first.getExprents(); - if (_try.getVars().size() != 1 || !"java/lang/NoSuchFieldError".equals(_try.getVars().get(0).getVartype().value) || - first.type != Statement.TYPE_BASICBLOCK || exprents == null || exprents.size() != 1 || exprents.get(0).type != Exprent.EXPRENT_ASSIGNMENT) { - break; - } - ass = (AssignmentExprent)exprents.get(0); - if (ass.getRight().type != Exprent.EXPRENT_CONST || (!(((ConstExprent)ass.getRight()).getValue() instanceof Integer)) || - ass.getLeft().type != Exprent.EXPRENT_ARRAY){ - break; - } - ArrayExprent array = (ArrayExprent)ass.getLeft(); - if (array.getArray().type != Exprent.EXPRENT_FIELD || !array.getArray().equals(mapField) || array.getIndex().type != Exprent.EXPRENT_INVOCATION) { - break; - } - invoc = (InvocationExprent)array.getIndex(); - if (!enumName.equals(invoc.getClassname()) || !"ordinal".equals(invoc.getName()) || !"()I".equals(invoc.getStringDescriptor()) || - invoc.getInstance().type != Exprent.EXPRENT_FIELD) { - break; - } - - FieldExprent enumField = (FieldExprent)invoc.getInstance(); - if (!enumName.equals(enumField.getClassname()) || !enumField.isStatic()) { - break; - } - - idToName.put((Integer)((ConstExprent)ass.getRight()).getValue(), enumField.getName()); - seq.replaceStatement(_try, getNewEmptyStatement()); - replace = true; - } - - if (replace) { - seq.replaceStatement(seq.getStats().get(x), getNewEmptyStatement()); - node.classStruct.enumSwitchMap.put(fieldName, idToName); - node.wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fieldName, "[I")); - } - x = y; - } - } - - private Statement getNewEmptyStatement() { - BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( - DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - bstat.setExprents(new ArrayList()); - return bstat; - } - private static void setLambdaVars(ClassNode parent, ClassNode child) { - - if (parent.wrapper == null || child.lambda_information.is_method_reference) { // method reference, no code and no parameters + if (child.lambdaInformation.is_method_reference) { // method reference, no code and no parameters return; } - final MethodWrapper meth = parent.wrapper.getMethods().getWithKey(child.lambda_information.content_method_key); - final MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); + MethodWrapper method = parent.getWrapper().getMethods().getWithKey(child.lambdaInformation.content_method_key); + MethodWrapper enclosingMethod = parent.getWrapper().getMethods().getWithKey(child.enclosingMethod); - MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(child.lambda_information.method_descriptor); - final MethodDescriptor md_content = MethodDescriptor.parseDescriptor(child.lambda_information.content_method_descriptor); + MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(child.lambdaInformation.method_descriptor); + MethodDescriptor md_content = MethodDescriptor.parseDescriptor(child.lambdaInformation.content_method_descriptor); - final int vars_count = md_content.params.length - md_lambda.params.length; - // if(vars_count < 0) { // should not happen, but just in case... - // vars_count = 0; - // } + int vars_count = md_content.params.length - md_lambda.params.length; + boolean is_static_lambda_content = child.lambdaInformation.is_content_method_static; - final boolean is_static_lambda_content = child.lambda_information.is_content_method_static; + String parent_class_name = parent.getWrapper().getClassStruct().qualifiedName; + String lambda_class_name = child.simpleName; - final String parent_class_name = parent.wrapper.getClassStruct().qualifiedName; - final String lambda_class_name = child.simpleName; - - final VarType lambda_class_type = new VarType(lambda_class_name, true); + VarType lambda_class_type = new VarType(lambda_class_name, true); // this pointer if (!is_static_lambda_content && DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) { - meth.varproc.getThisvars().put(new VarVersionPaar(0, 0), parent_class_name); - meth.varproc.setVarName(new VarVersionPaar(0, 0), parent.simpleName + ".this"); + method.varproc.getThisVars().put(new VarVersionPair(0, 0), parent_class_name); + method.varproc.setVarName(new VarVersionPair(0, 0), parent.simpleName + ".this"); } - // local variables - DirectGraph graph = encmeth.getOrBuildGraph(); + Map mapNewNames = new HashMap<>(); - final HashMap mapNewNames = new HashMap(); + enclosingMethod.getOrBuildGraph().iterateExprents(exprent -> { + List lst = exprent.getAllExprents(true); + lst.add(exprent); - graph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_NEW) { + NewExprent new_expr = (NewExprent)expr; - List lst = exprent.getAllExprents(true); - lst.add(exprent); + VarNamesCollector enclosingCollector = new VarNamesCollector(enclosingMethod.varproc.getVarNames()); - for (Exprent expr : lst) { + if (new_expr.isLambda() && lambda_class_type.equals(new_expr.getNewType())) { + InvocationExprent inv_dynamic = new_expr.getConstructor(); - if (expr.type == Exprent.EXPRENT_NEW) { - NewExprent new_expr = (NewExprent)expr; - VarNamesCollector enclosingCollector = new VarNamesCollector(encmeth.varproc.getVarNames()); - if (new_expr.isLambda() && lambda_class_type.equals(new_expr.getNewtype())) { + int param_index = is_static_lambda_content ? 0 : 1; + int varIndex = is_static_lambda_content ? 0 : 1; - InvocationExprent inv_dynamic = new_expr.getConstructor(); + for (int i = 0; i < md_content.params.length; ++i) { + VarVersionPair varVersion = new VarVersionPair(varIndex, 0); + if (i < vars_count) { + Exprent param = inv_dynamic.getLstParameters().get(param_index + i); - int param_index = is_static_lambda_content ? 0 : 1; - int varindex = is_static_lambda_content ? 0 : 1; - - for (int i = 0; i < md_content.params.length; ++i) { - VarVersionPaar varVersion = new VarVersionPaar(varindex, 0, ExprProcessor.getCastTypeName(md_content.params[i].copy()), false); - if (i < vars_count) { - Exprent param = inv_dynamic.getLstParameters().get(param_index + i); - - if (param.type == Exprent.EXPRENT_VAR) { - mapNewNames.put(varVersion, encmeth.varproc.getVarName(new VarVersionPaar((VarExprent) param))); - } - } else { - mapNewNames.put(varVersion, enclosingCollector.getFreeName(meth.varproc.getVarName(varVersion))); + if (param.type == Exprent.EXPRENT_VAR) { + mapNewNames.put(varVersion, enclosingMethod.varproc.getVarName(new VarVersionPair((VarExprent)param))); } - - varindex += md_content.params[i].stack_size; } + else { + mapNewNames.put(varVersion, enclosingCollector.getFreeName(method.varproc.getVarName(varVersion))); + } + + varIndex += md_content.params[i].stackSize; } } } - - return 0; } + + return 0; }); // update names of local variables - HashSet setNewOuterNames = new HashSet(mapNewNames.values()); - setNewOuterNames.removeAll(meth.setOuterVarNames); + Set setNewOuterNames = new HashSet<>(mapNewNames.values()); + setNewOuterNames.removeAll(method.setOuterVarNames); - meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); - meth.setOuterVarNames.addAll(setNewOuterNames); + method.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); + method.setOuterVarNames.addAll(setNewOuterNames); - for (Entry entr : mapNewNames.entrySet()) { - meth.varproc.setVarName(entr.getKey(), entr.getValue()); + for (Entry entry : mapNewNames.entrySet()) { + method.varproc.setVarName(entry.getKey(), entry.getValue()); } } private static void checkNotFoundClasses(ClassNode root, ClassNode node) { + List copy = new ArrayList<>(node.nested); - List lstChildren = new ArrayList(node.nested); - - for (ClassNode child : lstChildren) { + for (ClassNode child : copy) { + if (child.classStruct.isSynthetic()) { + continue; + } if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_ANONYMOUS) && child.enclosingMethod == null) { - Set setEnclosing = child.enclosingClasses; - if (setEnclosing.size() == 1) { - StructEnclosingMethodAttribute attr = - (StructEnclosingMethodAttribute)child.classStruct.getAttributes().getWithKey("EnclosingMethod"); - if (attr != null && attr.getMethodName() != null) { - if (node.classStruct.qualifiedName.equals(attr.getClassName()) && - node.classStruct.getMethod(attr.getMethodName(), attr.getMethodDescriptor()) != null) { - child.enclosingMethod = InterpreterUtil.makeUniqueKey(attr.getMethodName(), attr.getMethodDescriptor()); - continue; - } + if (!setEnclosing.isEmpty()) { + StructEnclosingMethodAttribute attr = child.classStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_ENCLOSING_METHOD); + if (attr != null && + attr.getMethodName() != null && + node.classStruct.qualifiedName.equals(attr.getClassName()) && + node.classStruct.getMethod(attr.getMethodName(), attr.getMethodDescriptor()) != null) { + child.enclosingMethod = InterpreterUtil.makeUniqueKey(attr.getMethodName(), attr.getMethodDescriptor()); + continue; } } @@ -346,10 +189,7 @@ public class NestedClassProcessor { child.parent = null; setEnclosing.remove(node.classStruct.qualifiedName); - boolean hasEnclosing = !setEnclosing.isEmpty(); - if (hasEnclosing) { - hasEnclosing = insertNestedClass(root, child); - } + boolean hasEnclosing = !setEnclosing.isEmpty() && insertNestedClass(root, child); if (!hasEnclosing) { if (child.type == ClassNode.CLASS_ANONYMOUS) { @@ -366,14 +206,12 @@ public class NestedClassProcessor { } private static boolean insertNestedClass(ClassNode root, ClassNode child) { - Set setEnclosing = child.enclosingClasses; - LinkedList stack = new LinkedList(); + LinkedList stack = new LinkedList<>(); stack.add(root); while (!stack.isEmpty()) { - ClassNode node = stack.removeFirst(); if (setEnclosing.contains(node.classStruct.qualifiedName)) { @@ -390,328 +228,294 @@ public class NestedClassProcessor { return false; } + private static void computeLocalVarsAndDefinitions(ClassNode node) { + // class name -> constructor descriptor -> var to field link + Map>> mapVarMasks = new HashMap<>(); - private static void computeLocalVarsAndDefinitions(final ClassNode node) { - - // local var masks - // class name, constructor descriptor, field mask - final HashMap>> mapVarMasks = new HashMap>>(); - - int cltypes = 0; + int clTypes = 0; for (ClassNode nd : node.nested) { - if (nd.type != ClassNode.CLASS_LAMBDA) { - if ((nd.access & CodeConstants.ACC_STATIC) == 0 && (nd.access & CodeConstants.ACC_INTERFACE) == 0) { + if (nd.type != ClassNode.CLASS_LAMBDA && + !nd.classStruct.isSynthetic() && + (nd.access & CodeConstants.ACC_STATIC) == 0 && + (nd.access & CodeConstants.ACC_INTERFACE) == 0) { + clTypes |= nd.type; - cltypes |= nd.type; - - HashMap> mask = getMaskLocalVars(nd.wrapper); - if (mask.isEmpty()) { - String message = "Nested class " + nd.classStruct.qualifiedName + " has no constructor!"; - DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); - } - else { - mapVarMasks.put(nd.classStruct.qualifiedName, mask); - } + Map> mask = getMaskLocalVars(nd.getWrapper()); + if (mask.isEmpty()) { + String message = "Nested class " + nd.classStruct.qualifiedName + " has no constructor!"; + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); + } + else { + mapVarMasks.put(nd.classStruct.qualifiedName, mask); } } } // local var masks - final HashMap>> mapVarFieldPairs = - new HashMap>>(); - - if (cltypes != ClassNode.CLASS_MEMBER) { + Map>> mapVarFieldPairs = new HashMap<>(); + if (clTypes != ClassNode.CLASS_MEMBER) { // iterate enclosing class - for (final MethodWrapper meth : node.wrapper.getMethods()) { + for (MethodWrapper method : node.getWrapper().getMethods()) { + if (method.root != null) { // neither abstract, nor native + method.getOrBuildGraph().iterateExprents(exprent -> { + List lst = exprent.getAllExprents(true); + lst.add(exprent); - if (meth.root != null) { // neither abstract, nor native - DirectGraph graph = meth.getOrBuildGraph(); + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_NEW) { + InvocationExprent constructor = ((NewExprent)expr).getConstructor(); - graph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - List lst = exprent.getAllExprents(true); - lst.add(exprent); + if (constructor != null && mapVarMasks.containsKey(constructor.getClassname())) { // non-static inner class constructor + String refClassName = constructor.getClassname(); + ClassNode nestedClassNode = node.getClassNode(refClassName); - for (Exprent expr : lst) { + if (nestedClassNode.type != ClassNode.CLASS_MEMBER) { + List mask = mapVarMasks.get(refClassName).get(constructor.getStringDescriptor()); - if (expr.type == Exprent.EXPRENT_NEW) { - InvocationExprent constr = ((NewExprent)expr).getConstructor(); - - if (constr != null && mapVarMasks.containsKey(constr.getClassname())) { // non-static inner class constructor - - String refclname = constr.getClassname(); - - ClassNode nestedClassNode = node.getClassNode(refclname); - - if (nestedClassNode.type != ClassNode.CLASS_MEMBER) { - - List mask = mapVarMasks.get(refclname).get(constr.getStringDescriptor()); - - if (!mapVarFieldPairs.containsKey(refclname)) { - mapVarFieldPairs.put(refclname, new HashMap>()); - } - - List lstTemp = new ArrayList(); - - for (int i = 0; i < mask.size(); i++) { - Exprent param = constr.getLstParameters().get(i); - VarFieldPair pair = null; - - if (param.type == Exprent.EXPRENT_VAR && mask.get(i) != null) { - VarVersionPaar varpaar = new VarVersionPaar((VarExprent)param); - - // FIXME: final flags of variables are wrong! Correct the entire final functionality. - // if(meth.varproc.getVarFinal(varpaar) != VarTypeProcessor.VAR_NONFINAL) { - pair = new VarFieldPair(mask.get(i).keyfield, varpaar); - // } - } - - lstTemp.add(pair); - } - - List pairmask = mapVarFieldPairs.get(refclname).get(constr.getStringDescriptor()); - - if (pairmask == null) { - pairmask = lstTemp; - } - else { - for (int i = 0; i < pairmask.size(); i++) { - if (!InterpreterUtil.equalObjects(pairmask.get(i), lstTemp.get(i))) { - pairmask.set(i, null); - } - } - } - - mapVarFieldPairs.get(refclname).put(constr.getStringDescriptor(), pairmask); - nestedClassNode.enclosingMethod = - InterpreterUtil.makeUniqueKey(meth.methodStruct.getName(), meth.methodStruct.getDescriptor()); + if (!mapVarFieldPairs.containsKey(refClassName)) { + mapVarFieldPairs.put(refClassName, new HashMap<>()); } + + List lstTemp = new ArrayList<>(); + + for (int i = 0; i < mask.size(); i++) { + Exprent param = constructor.getLstParameters().get(i); + VarFieldPair pair = null; + + if (param.type == Exprent.EXPRENT_VAR && mask.get(i) != null) { + VarVersionPair varPair = new VarVersionPair((VarExprent)param); + + // FIXME: flags of variables are wrong! Correct the entire functionality. + // if(method.varproc.getVarFinal(varPair) != VarTypeProcessor.VAR_NON_FINAL) { + pair = new VarFieldPair(mask.get(i).fieldKey, varPair); + // } + } + + lstTemp.add(pair); + } + + List pairMask = mapVarFieldPairs.get(refClassName).get(constructor.getStringDescriptor()); + if (pairMask == null) { + pairMask = lstTemp; + } + else { + for (int i = 0; i < pairMask.size(); i++) { + if (!InterpreterUtil.equalObjects(pairMask.get(i), lstTemp.get(i))) { + pairMask.set(i, null); + } + } + } + + mapVarFieldPairs.get(refClassName).put(constructor.getStringDescriptor(), pairMask); + nestedClassNode.enclosingMethod = + InterpreterUtil.makeUniqueKey(method.methodStruct.getName(), method.methodStruct.getDescriptor()); } } } - return 0; } + + return 0; }); } } } // merge var masks - for (Entry>> entcl : mapVarMasks.entrySet()) { - - ClassNode nestedNode = node.getClassNode(entcl.getKey()); + for (Entry>> enclosing : mapVarMasks.entrySet()) { + ClassNode nestedNode = node.getClassNode(enclosing.getKey()); // intersection - List intrPairMask = null; + List interPairMask = null; // merge referenced constructors - if (mapVarFieldPairs.containsKey(entcl.getKey())) { - for (List mask : mapVarFieldPairs.get(entcl.getKey()).values()) { - if (intrPairMask == null) { - intrPairMask = new ArrayList(mask); + if (mapVarFieldPairs.containsKey(enclosing.getKey())) { + for (List mask : mapVarFieldPairs.get(enclosing.getKey()).values()) { + if (interPairMask == null) { + interPairMask = new ArrayList<>(mask); } else { - mergeListSignatures(intrPairMask, mask, false); + mergeListSignatures(interPairMask, mask, false); } } } - List intrMask = null; + List interMask = null; // merge all constructors - for (List mask : entcl.getValue().values()) { - if (intrMask == null) { - intrMask = new ArrayList(mask); + for (List mask : enclosing.getValue().values()) { + if (interMask == null) { + interMask = new ArrayList<>(mask); } else { - mergeListSignatures(intrMask, mask, false); + mergeListSignatures(interMask, mask, false); } } - if (intrPairMask == null) { // member or local and never instantiated - intrPairMask = new ArrayList(intrMask); + if (interPairMask == null) { // member or local and never instantiated + interPairMask = interMask != null ? new ArrayList<>(interMask) : new ArrayList<>(); boolean found = false; - for (int i = 0; i < intrPairMask.size(); i++) { - if (intrPairMask.get(i) != null) { + for (int i = 0; i < interPairMask.size(); i++) { + if (interPairMask.get(i) != null) { if (found) { - intrPairMask.set(i, null); + interPairMask.set(i, null); } found = true; } } } - mergeListSignatures(intrPairMask, intrMask, true); + mergeListSignatures(interPairMask, interMask, true); - for (int i = 0; i < intrPairMask.size(); i++) { - VarFieldPair pair = intrPairMask.get(i); - if (pair != null && pair.keyfield.length() > 0) { - nestedNode.mapFieldsToVars.put(pair.keyfield, pair.varpaar); + for (VarFieldPair pair : interPairMask) { + if (pair != null && !pair.fieldKey.isEmpty()) { + nestedNode.mapFieldsToVars.put(pair.fieldKey, pair.varPair); } } // set resulting constructor signatures - for (Entry> entmt : entcl.getValue().entrySet()) { - mergeListSignatures(entmt.getValue(), intrPairMask, false); + for (Entry> entry : enclosing.getValue().entrySet()) { + mergeListSignatures(entry.getValue(), interPairMask, false); - MethodWrapper meth = nestedNode.wrapper.getMethodWrapper("", entmt.getKey()); - meth.signatureFields = new ArrayList(); - - for (VarFieldPair pair : entmt.getValue()) { - meth.signatureFields.add(pair == null ? null : pair.varpaar); + List mask = new ArrayList<>(entry.getValue().size()); + for (VarFieldPair pair : entry.getValue()) { + mask.add(pair != null && !pair.fieldKey.isEmpty() ? pair.varPair : null); } + nestedNode.getWrapper().getMethodWrapper(CodeConstants.INIT_NAME, entry.getKey()).synthParameters = mask; } } } - private static void insertLocalVars(final ClassNode parent, final ClassNode child) { - + private static void insertLocalVars(ClassNode parent, ClassNode child) { // enclosing method, is null iff member class - MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); + MethodWrapper enclosingMethod = parent.getWrapper().getMethods().getWithKey(child.enclosingMethod); // iterate all child methods - for (final MethodWrapper meth : child.wrapper.getMethods()) { + for (MethodWrapper method : child.getWrapper().getMethods()) { + if (method.root != null) { // neither abstract nor native + Map mapNewNames = new HashMap<>(); // local var names + Map mapNewTypes = new HashMap<>(); // local var types - if (meth.root != null) { // neither abstract nor native + Map mapParamsToNewVars = new HashMap<>(); + if (method.synthParameters != null) { + int index = 0, varIndex = 1; + MethodDescriptor md = MethodDescriptor.parseDescriptor(method.methodStruct.getDescriptor()); - // local var names - HashMap mapNewNames = new HashMap(); - // local var types - HashMap mapNewTypes = new HashMap(); + for (VarVersionPair pair : method.synthParameters) { + if (pair != null) { + VarVersionPair newVar = new VarVersionPair(method.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); - final HashMap mapParamsToNewVars = new HashMap(); - if (meth.signatureFields != null) { - int index = 0; - int varindex = 1; - MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor()); + mapParamsToNewVars.put(varIndex, newVar); - for (VarVersionPaar paar : meth.signatureFields) { - if (paar != null) { - VarVersionPaar newvar = new VarVersionPaar(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); - - mapParamsToNewVars.put(varindex, newvar); - - String varname = null; - VarType vartype = null; + String varName = null; + VarType varType = null; if (child.type != ClassNode.CLASS_MEMBER) { - varname = encmeth.varproc.getVarName(paar); - vartype = encmeth.varproc.getVarType(paar); + varName = enclosingMethod.varproc.getVarName(pair); + varType = enclosingMethod.varproc.getVarType(pair); - encmeth.varproc.setVarFinal(paar, VarTypeProcessor.VAR_FINALEXPLICIT); + enclosingMethod.varproc.setVarFinal(pair, VarTypeProcessor.VAR_EXPLICIT_FINAL); } - if (paar.var == -1 || "this".equals(varname)) { + if (pair.var == -1 || "this".equals(varName)) { if (parent.simpleName == null) { // anonymous enclosing class, no access to this - varname = VarExprent.VAR_NAMELESS_ENCLOSURE; + varName = VarExprent.VAR_NAMELESS_ENCLOSURE; } else { - varname = parent.simpleName + ".this"; + varName = parent.simpleName + ".this"; } - meth.varproc.getThisvars().put(newvar, parent.classStruct.qualifiedName); + method.varproc.getThisVars().put(newVar, parent.classStruct.qualifiedName); } - mapNewNames.put(newvar, varname); - mapNewTypes.put(newvar, vartype); + mapNewNames.put(newVar, varName); + mapNewTypes.put(newVar, varType); } - varindex += md.params[index++].stack_size; + + varIndex += md.params[index++].stackSize; } } - // new vars - final HashMap mapFieldsToNewVars = new HashMap(); + Map mapFieldsToNewVars = new HashMap<>(); + for (ClassNode classNode = child; classNode != null; classNode = classNode.parent) { + for (Entry entry : classNode.mapFieldsToVars.entrySet()) { + VarVersionPair newVar = new VarVersionPair(method.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); - for (ClassNode clnode = child; clnode != null; clnode = clnode.parent) { + mapFieldsToNewVars.put(InterpreterUtil.makeUniqueKey(classNode.classStruct.qualifiedName, entry.getKey()), newVar); - for (Entry entr : clnode.mapFieldsToVars.entrySet()) { - VarVersionPaar newvar = new VarVersionPaar(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); + String varName = null; + VarType varType = null; - mapFieldsToNewVars.put(InterpreterUtil.makeUniqueKey(clnode.classStruct.qualifiedName, entr.getKey()), newvar); + if (classNode.type != ClassNode.CLASS_MEMBER) { + MethodWrapper enclosing_method = classNode.parent.getWrapper().getMethods().getWithKey(classNode.enclosingMethod); - String varname = null; - VarType vartype = null; + varName = enclosing_method.varproc.getVarName(entry.getValue()); + varType = enclosing_method.varproc.getVarType(entry.getValue()); - if (clnode.type != ClassNode.CLASS_MEMBER) { - - MethodWrapper enclosing_method = clnode.parent.wrapper.getMethods().getWithKey(clnode.enclosingMethod); - - varname = enclosing_method.varproc.getVarName(entr.getValue()); - vartype = enclosing_method.varproc.getVarType(entr.getValue()); - - enclosing_method.varproc.setVarFinal(entr.getValue(), VarTypeProcessor.VAR_FINALEXPLICIT); + enclosing_method.varproc.setVarFinal(entry.getValue(), VarTypeProcessor.VAR_EXPLICIT_FINAL); } - if (entr.getValue().var == -1 || "this".equals(varname)) { - if (clnode.parent.simpleName == null) { + if (entry.getValue().var == -1 || "this".equals(varName)) { + if (classNode.parent.simpleName == null) { // anonymous enclosing class, no access to this - varname = VarExprent.VAR_NAMELESS_ENCLOSURE; + varName = VarExprent.VAR_NAMELESS_ENCLOSURE; } else { - varname = clnode.parent.simpleName + ".this"; + varName = classNode.parent.simpleName + ".this"; } - meth.varproc.getThisvars().put(newvar, clnode.parent.classStruct.qualifiedName); + method.varproc.getThisVars().put(newVar, classNode.parent.classStruct.qualifiedName); } - mapNewNames.put(newvar, varname); - mapNewTypes.put(newvar, vartype); + mapNewNames.put(newVar, varName); + mapNewTypes.put(newVar, varType); // hide synthetic field - if (clnode == child) { // fields higher up the chain were already handled with their classes - StructField fd = child.classStruct.getFields().getWithKey(entr.getKey()); - child.wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); + if (classNode == child) { // fields higher up the chain were already handled with their classes + StructField fd = child.classStruct.getFields().getWithKey(entry.getKey()); + child.getWrapper().getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); } } } - HashSet setNewOuterNames = new HashSet(mapNewNames.values()); - setNewOuterNames.removeAll(meth.setOuterVarNames); + Set setNewOuterNames = new HashSet<>(mapNewNames.values()); + setNewOuterNames.removeAll(method.setOuterVarNames); - meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); - meth.setOuterVarNames.addAll(setNewOuterNames); + method.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); + method.setOuterVarNames.addAll(setNewOuterNames); - for (Entry entr : mapNewNames.entrySet()) { - VarVersionPaar varpaar = entr.getKey(); - VarType vartype = mapNewTypes.get(varpaar); + for (Entry entry : mapNewNames.entrySet()) { + VarVersionPair pair = entry.getKey(); + VarType type = mapNewTypes.get(pair); - meth.varproc.setVarName(varpaar, entr.getValue()); - if (vartype != null) { - meth.varproc.setVarType(varpaar, vartype); + method.varproc.setVarName(pair, entry.getValue()); + if (type != null) { + method.varproc.setVarType(pair, type); } } - DirectGraph graph = meth.getOrBuildGraph(); - - graph.iterateExprents(new DirectGraph.ExprentIterator() { + method.getOrBuildGraph().iterateExprents(new DirectGraph.ExprentIterator() { + @Override public int processExprent(Exprent exprent) { - if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asexpr = (AssignmentExprent)exprent; - if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { - FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); - - if (fexpr.getClassname().equals(child.classStruct.qualifiedName) && // process this class only - mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(child.classStruct.qualifiedName, - InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr - .getDescriptor().descriptorString)))) { + AssignmentExprent assignExpr = (AssignmentExprent)exprent; + if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) { + FieldExprent fExpr = (FieldExprent)assignExpr.getLeft(); + String qName = child.classStruct.qualifiedName; + if (fExpr.getClassname().equals(qName) && // process this class only + mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(qName, fExpr.getName(), fExpr.getDescriptor().descriptorString))) { return 2; } - - //if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && - // mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString))) { - // return 2; - //} } } - if (child.type == ClassNode.CLASS_ANONYMOUS && "".equals(meth.methodStruct.getName()) - && exprent.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent invexpr = (InvocationExprent)exprent; - if (invexpr.getFunctype() == InvocationExprent.TYP_INIT) { + if (child.type == ClassNode.CLASS_ANONYMOUS && + CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && + exprent.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent invokeExpr = (InvocationExprent)exprent; + if (invokeExpr.getFunctype() == InvocationExprent.TYP_INIT) { // invocation of the super constructor in an anonymous class - child.superInvocation = invexpr; // FIXME: save original names of parameters + child.superInvocation = invokeExpr; // FIXME: save original names of parameters return 2; } } @@ -722,27 +526,23 @@ public class NestedClassProcessor { } private Exprent replaceExprent(Exprent exprent) { - if (exprent.type == Exprent.EXPRENT_VAR) { - int varindex = ((VarExprent)exprent).getIndex(); - if (mapParamsToNewVars.containsKey(varindex)) { - VarVersionPaar newvar = mapParamsToNewVars.get(varindex); - meth.varproc.getExternvars().add(newvar); - return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); + int varIndex = ((VarExprent)exprent).getIndex(); + if (mapParamsToNewVars.containsKey(varIndex)) { + VarVersionPair newVar = mapParamsToNewVars.get(varIndex); + method.varproc.getExternalVars().add(newVar); + return new VarExprent(newVar.var, method.varproc.getVarType(newVar), method.varproc); } } else if (exprent.type == Exprent.EXPRENT_FIELD) { - FieldExprent fexpr = (FieldExprent)exprent; - - String keyField = InterpreterUtil.makeUniqueKey(fexpr.getClassname(), InterpreterUtil - .makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString)); - - if (mapFieldsToNewVars.containsKey(keyField)) { - //if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && - // mapFieldsToNewVars.containsKey(keyField)) { - VarVersionPaar newvar = mapFieldsToNewVars.get(keyField); - meth.varproc.getExternvars().add(newvar); - return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); + FieldExprent fExpr = (FieldExprent)exprent; + String key = InterpreterUtil.makeUniqueKey(fExpr.getClassname(), fExpr.getName(), fExpr.getDescriptor().descriptorString); + if (mapFieldsToNewVars.containsKey(key)) { + //if(fExpr.getClassname().equals(child.classStruct.qualifiedName) && + // mapFieldsToNewVars.containsKey(key)) { + VarVersionPair newVar = mapFieldsToNewVars.get(key); + method.varproc.getExternalVars().add(newVar); + return new VarExprent(newVar.var, method.varproc.getVarType(newVar), method.varproc); } } @@ -751,9 +551,9 @@ public class NestedClassProcessor { replaced = false; for (Exprent expr : exprent.getAllExprents()) { - Exprent retexpr = replaceExprent(expr); - if (retexpr != null) { - exprent.replaceExprent(expr, retexpr); + Exprent retExpr = replaceExprent(expr); + if (retExpr != null) { + exprent.replaceExprent(expr, retExpr); replaced = true; break; } @@ -767,30 +567,28 @@ public class NestedClassProcessor { } } - private static HashMap> getMaskLocalVars(ClassWrapper wrapper) { - - HashMap> mapMasks = new HashMap>(); + private static Map> getMaskLocalVars(ClassWrapper wrapper) { + Map> mapMasks = new HashMap<>(); StructClass cl = wrapper.getClassStruct(); // iterate over constructors for (StructMethod mt : cl.getMethods()) { - if ("".equals(mt.getName())) { - + if (CodeConstants.INIT_NAME.equals(mt.getName())) { MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); - - MethodWrapper meth = wrapper.getMethodWrapper("", mt.getDescriptor()); - DirectGraph graph = meth.getOrBuildGraph(); + MethodWrapper method = wrapper.getMethodWrapper(CodeConstants.INIT_NAME, mt.getDescriptor()); + DirectGraph graph = method.getOrBuildGraph(); if (graph != null) { // something gone wrong, should not be null - List fields = new ArrayList(); + List fields = new ArrayList<>(md.params.length); - int varindex = 1; + int varIndex = 1; for (int i = 0; i < md.params.length; i++) { // no static methods allowed - String keyField = getEnclosingVarField(cl, meth, graph, varindex); - fields.add(keyField == null ? null : new VarFieldPair(keyField, new VarVersionPaar(-1, 0))); // TODO: null? - varindex += md.params[i].stack_size; + String keyField = getEnclosingVarField(cl, method, graph, varIndex); + fields.add(keyField == null ? null : new VarFieldPair(keyField, new VarVersionPair(-1, 0))); // TODO: null? + varIndex += md.params[i].stackSize; } + mapMasks.put(mt.getDescriptor(), fields); } } @@ -799,38 +597,34 @@ public class NestedClassProcessor { return mapMasks; } - private static String getEnclosingVarField(StructClass cl, MethodWrapper meth, DirectGraph graph, final int index) { - + private static String getEnclosingVarField(StructClass cl, MethodWrapper method, DirectGraph graph, int index) { String field = ""; // parameter variable final - if (meth.varproc.getVarFinal(new VarVersionPaar(index, 0)) == VarTypeProcessor.VAR_NONFINAL) { + if (method.varproc.getVarFinal(new VarVersionPair(index, 0)) == VarTypeProcessor.VAR_NON_FINAL) { return null; } boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); // no loop at the begin - DirectNode firstnode = graph.first; - if (firstnode.preds.isEmpty()) { - // assignment to a final synthetic field? - for (Exprent exprent : firstnode.exprents) { + DirectNode firstNode = graph.first; + if (firstNode.preds.isEmpty()) { + // assignment to a synthetic field? + for (Exprent exprent : firstNode.exprents) { if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent asexpr = (AssignmentExprent)exprent; - if (asexpr.getRight().type == Exprent.EXPRENT_VAR && ((VarExprent)asexpr.getRight()).getIndex() == index) { - if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { - - FieldExprent left = (FieldExprent)asexpr.getLeft(); - StructField fd = cl.getField(left.getName(), left.getDescriptor().descriptorString); - - if (fd != null) { // local (== not inherited) field - if (cl.qualifiedName.equals(left.getClassname()) && - fd.hasModifier(CodeConstants.ACC_FINAL) && - (fd.isSynthetic() || (noSynthFlag && fd.hasModifier(CodeConstants.ACC_PRIVATE)))) { - field = InterpreterUtil.makeUniqueKey(left.getName(), left.getDescriptor().descriptorString); - break; - } - } + AssignmentExprent assignExpr = (AssignmentExprent)exprent; + if (assignExpr.getRight().type == Exprent.EXPRENT_VAR && + ((VarExprent)assignExpr.getRight()).getIndex() == index && + assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) { + FieldExprent left = (FieldExprent)assignExpr.getLeft(); + StructField fd = cl.getField(left.getName(), left.getDescriptor().descriptorString); + if (fd != null && + cl.qualifiedName.equals(left.getClassname()) && + (fd.isSynthetic() || noSynthFlag && possiblySyntheticField(fd))) { + // local (== not inherited) field + field = InterpreterUtil.makeUniqueKey(left.getName(), left.getDescriptor().descriptorString); + break; } } } @@ -840,52 +634,32 @@ public class NestedClassProcessor { return field; } + private static boolean possiblySyntheticField(StructField fd) { + return fd.getName().contains("$") && fd.hasModifier(CodeConstants.ACC_FINAL) && fd.hasModifier(CodeConstants.ACC_PRIVATE); + } + private static void mergeListSignatures(List first, List second, boolean both) { - int i = 1; - while (true) { - if (first.size() <= i || second.size() <= i) { - break; - } - VarFieldPair fobj = first.get(first.size() - i); - VarFieldPair sobj = second.get(second.size() - i); + while (first.size() > i && second.size() > i) { + VarFieldPair fObj = first.get(first.size() - i); + VarFieldPair sObj = second.get(second.size() - i); - boolean eq = false; - if (fobj == null || sobj == null) { - eq = (fobj == sobj); - } - else { - eq = true; - if (fobj.keyfield.length() == 0) { - fobj.keyfield = sobj.keyfield; - } - else if (sobj.keyfield.length() == 0) { - if (both) { - sobj.keyfield = fobj.keyfield; - } - } - else { - eq = fobj.keyfield.equals(sobj.keyfield); - } - } - - if (!eq) { + if (!isEqual(both, fObj, sObj)) { first.set(first.size() - i, null); if (both) { second.set(second.size() - i, null); } } - else { - if (fobj != null) { - if (fobj.varpaar.var == -1) { - fobj.varpaar = sobj.varpaar; - } - else { - sobj.varpaar = fobj.varpaar; - } + else if (fObj != null) { + if (fObj.varPair.var == -1) { + fObj.varPair = sObj.varPair; + } + else { + sObj.varPair = fObj.varPair; } } + i++; } @@ -909,64 +683,65 @@ public class NestedClassProcessor { first.set(0, null); } else { - VarFieldPair fobj = first.get(0); - VarFieldPair sobj = second.get(0); + VarFieldPair fObj = first.get(0); + VarFieldPair sObj = second.get(0); - boolean eq = false; - if (fobj == null || sobj == null) { - eq = (fobj == sobj); - } - else { - eq = true; - if (fobj.keyfield.length() == 0) { - fobj.keyfield = sobj.keyfield; - } - else if (sobj.keyfield.length() == 0) { - if (both) { - sobj.keyfield = fobj.keyfield; - } - } - else { - eq = fobj.keyfield.equals(sobj.keyfield); - } - } - - if (!eq) { + if (!isEqual(both, fObj, sObj)) { first.set(0, null); if (both) { second.set(0, null); } } - else if (fobj != null) { - if (fobj.varpaar.var == -1) { - fobj.varpaar = sobj.varpaar; + else if (fObj != null) { + if (fObj.varPair.var == -1) { + fObj.varPair = sObj.varPair; } else { - sobj.varpaar = fobj.varpaar; + sObj.varPair = fObj.varPair; } } } } + private static boolean isEqual(boolean both, VarFieldPair fObj, VarFieldPair sObj) { + boolean eq; + if (fObj == null || sObj == null) { + eq = (fObj == sObj); + } + else { + eq = true; + if (fObj.fieldKey.length() == 0) { + fObj.fieldKey = sObj.fieldKey; + } + else if (sObj.fieldKey.length() == 0) { + if (both) { + sObj.fieldKey = fObj.fieldKey; + } + } + else { + eq = fObj.fieldKey.equals(sObj.fieldKey); + } + } + return eq; + } - private static void setLocalClassDefinition(MethodWrapper meth, ClassNode node) { + private static void setLocalClassDefinition(MethodWrapper method, ClassNode node) { + RootStatement root = method.root; - RootStatement root = meth.root; + Set setStats = new HashSet<>(); + VarType classType = new VarType(node.classStruct.qualifiedName, true); - HashSet setStats = new HashSet(); - VarType classtype = new VarType(node.classStruct.qualifiedName, true); - - Statement stdef = getDefStatement(root, classtype, setStats); - if (stdef == null) { + Statement statement = getDefStatement(root, classType, setStats); + if (statement == null) { // unreferenced local class - stdef = root.getFirst(); + statement = root.getFirst(); } - Statement first = findFirstBlock(stdef, setStats); + Statement first = findFirstBlock(statement, setStats); List lst; if (first == null) { - lst = stdef.getVarDefinitions(); + lst = statement.getVarDefinitions(); } else if (first.getExprents() == null) { lst = first.getVarDefinitions(); @@ -975,57 +750,47 @@ public class NestedClassProcessor { lst = first.getExprents(); } - - int addindex = 0; + int addIndex = 0; for (Exprent expr : lst) { - if (searchForClass(expr, classtype)) { + if (searchForClass(expr, classType)) { break; } - addindex++; + addIndex++; } - VarExprent var = new VarExprent(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), - classtype, meth.varproc); + VarExprent var = new VarExprent(method.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), classType, method.varproc); var.setDefinition(true); - var.setClassdef(true); + var.setClassDef(true); - lst.add(addindex, var); + lst.add(addIndex, var); } - - private static Statement findFirstBlock(Statement stat, HashSet setStats) { - - LinkedList stack = new LinkedList(); + private static Statement findFirstBlock(Statement stat, Set setStats) { + LinkedList stack = new LinkedList<>(); stack.add(stat); while (!stack.isEmpty()) { Statement st = stack.remove(0); if (stack.isEmpty() || setStats.contains(st)) { - - if (st.isLabeled() && !stack.isEmpty()) { + if (st.isLabeled() && !stack.isEmpty() || st.getExprents() != null) { return st; } - if (st.getExprents() != null) { - return st; - } - else { - stack.clear(); + stack.clear(); - switch (st.type) { - case Statement.TYPE_SEQUENCE: - stack.addAll(0, st.getStats()); - break; - case Statement.TYPE_IF: - case Statement.TYPE_ROOT: - case Statement.TYPE_SWITCH: - case Statement.TYPE_SYNCRONIZED: - stack.add(st.getFirst()); - break; - default: - return st; - } + switch (st.type) { + case Statement.TYPE_SEQUENCE: + stack.addAll(0, st.getStats()); + break; + case Statement.TYPE_IF: + case Statement.TYPE_ROOT: + case Statement.TYPE_SWITCH: + case Statement.TYPE_SYNCRONIZED: + stack.add(st.getFirst()); + break; + default: + return st; } } } @@ -1033,11 +798,9 @@ public class NestedClassProcessor { return null; } - - private static Statement getDefStatement(Statement stat, VarType classtype, HashSet setStats) { - - List condlst = new ArrayList(); - Statement retstat = null; + private static Statement getDefStatement(Statement stat, VarType classType, Set setStats) { + List lst = new ArrayList<>(); + Statement retStat = null; if (stat.getExprents() == null) { int counter = 0; @@ -1046,65 +809,63 @@ public class NestedClassProcessor { if (obj instanceof Statement) { Statement st = (Statement)obj; - Statement stTemp = getDefStatement(st, classtype, setStats); + Statement stTemp = getDefStatement(st, classType, setStats); if (stTemp != null) { if (counter == 1) { - retstat = stat; + retStat = stat; break; } - retstat = stTemp; + retStat = stTemp; counter++; } - if (st.type == DoStatement.TYPE_DO) { + if (st.type == Statement.TYPE_DO) { DoStatement dost = (DoStatement)st; - condlst.addAll(dost.getInitExprentList()); - condlst.addAll(dost.getConditionExprentList()); + lst.addAll(dost.getInitExprentList()); + lst.addAll(dost.getConditionExprentList()); } } else if (obj instanceof Exprent) { - condlst.add((Exprent)obj); + lst.add((Exprent)obj); } } } else { - condlst = stat.getExprents(); + lst = stat.getExprents(); } - if (retstat != stat) { - for (Exprent exprent : condlst) { - if (exprent != null && searchForClass(exprent, classtype)) { - retstat = stat; + if (retStat != stat) { + for (Exprent exprent : lst) { + if (exprent != null && searchForClass(exprent, classType)) { + retStat = stat; break; } } } - if (retstat != null) { + if (retStat != null) { setStats.add(stat); } - return retstat; + return retStat; } - private static boolean searchForClass(Exprent exprent, VarType classtype) { - + private static boolean searchForClass(Exprent exprent, VarType classType) { List lst = exprent.getAllExprents(true); lst.add(exprent); - String classname = classtype.value; + String classname = classType.value; for (Exprent expr : lst) { - boolean res = false; switch (expr.type) { case Exprent.EXPRENT_CONST: - ConstExprent cexpr = (ConstExprent)expr; - res = (VarType.VARTYPE_CLASS.equals(cexpr.getConsttype()) && classname.equals(cexpr.getValue()) || - classtype.equals(cexpr.getConsttype())); + ConstExprent constExpr = (ConstExprent)expr; + res = (VarType.VARTYPE_CLASS.equals(constExpr.getConstType()) && classname.equals(constExpr.getValue()) || + classType.equals(constExpr.getConstType())); break; case Exprent.EXPRENT_FIELD: res = classname.equals(((FieldExprent)expr).getClassname()); @@ -1117,10 +878,10 @@ public class NestedClassProcessor { res = newType.type == CodeConstants.TYPE_OBJECT && classname.equals(newType.value); break; case Exprent.EXPRENT_VAR: - VarExprent vexpr = (VarExprent)expr; - if (vexpr.isDefinition()) { - VarType vtype = vexpr.getVartype(); - if (classtype.equals(vtype) || (vtype.arraydim > 0 && classtype.value.equals(vtype.value))) { + VarExprent varExpr = (VarExprent)expr; + if (varExpr.isDefinition()) { + VarType varType = varExpr.getVarType(); + if (classType.equals(varType) || (varType.arrayDim > 0 && classType.value.equals(varType.value))) { res = true; } } @@ -1134,29 +895,27 @@ public class NestedClassProcessor { return false; } - private static class VarFieldPair { + public String fieldKey; + public VarVersionPair varPair; - public String keyfield = ""; - public VarVersionPaar varpaar; - - public VarFieldPair(String field, VarVersionPaar varpaar) { - this.keyfield = field; - this.varpaar = varpaar; + VarFieldPair(String field, VarVersionPair varPair) { + this.fieldKey = field; + this.varPair = varPair; } @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof VarFieldPair)) return false; + if (!(o instanceof VarFieldPair)) return false; VarFieldPair pair = (VarFieldPair)o; - return keyfield.equals(pair.keyfield) && varpaar.equals(pair.varpaar); + return fieldKey.equals(pair.fieldKey) && varPair.equals(pair.varPair); } @Override public int hashCode() { - return keyfield.hashCode() + varpaar.hashCode(); + return fieldKey.hashCode() + varPair.hashCode(); } } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java b/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java index 7e5eaa5..692a8b9 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.rels; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -24,25 +10,19 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.util.InterpreterUtil; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Map; +import java.util.*; public class NestedMemberAccess { - private static final int METHOD_ACCESS_NORMAL = 1; - private static final int METHOD_ACCESS_FIELD_GET = 2; - private static final int METHOD_ACCESS_FIELD_SET = 3; - private static final int METHOD_ACCESS_METHOD = 4; + private enum MethodAccess {NORMAL, FIELD_GET, FIELD_SET, METHOD, FUNCTION} private boolean noSynthFlag; - private Map mapMethodType = new HashMap(); + private final Map mapMethodType = new HashMap<>(); public void propagateMemberAccess(ClassNode root) { @@ -67,13 +47,13 @@ public class NestedMemberAccess { computeMethodTypes(nd); } - for (MethodWrapper method : node.wrapper.getMethods()) { + for (MethodWrapper method : node.getWrapper().getMethods()) { computeMethodType(node, method); } } private void computeMethodType(ClassNode node, MethodWrapper method) { - int type = METHOD_ACCESS_NORMAL; + MethodAccess type = MethodAccess.NORMAL; if (method.root != null) { DirectGraph graph = method.getOrBuildGraph(); @@ -91,7 +71,7 @@ public class NestedMemberAccess { if (exprent.type == Exprent.EXPRENT_EXIT) { ExitExprent exexpr = (ExitExprent)exprent; - if (exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) { + if (exexpr.getExitType() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) { exprCore = exexpr.getValue(); } } @@ -104,7 +84,7 @@ public class NestedMemberAccess { if (fexpr.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field if (fexpr.isStatic() || (fexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpr.getInstance()).getIndex() == 0)) { - type = METHOD_ACCESS_FIELD_GET; + type = MethodAccess.FIELD_GET; } } } @@ -113,13 +93,23 @@ public class NestedMemberAccess { if (parcount == 1) { // this or final variable if (((VarExprent)exprCore).getIndex() != 0) { - type = METHOD_ACCESS_FIELD_GET; + type = MethodAccess.FIELD_GET; } } + break; + case Exprent.EXPRENT_FUNCTION: + // for now detect only increment/decrement + FunctionExprent functionExprent = (FunctionExprent)exprCore; + if (functionExprent.getFuncType() >= FunctionExprent.FUNCTION_IMM && + functionExprent.getFuncType() <= FunctionExprent.FUNCTION_PPI) { + if (functionExprent.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { + type = MethodAccess.FUNCTION; + } + } break; case Exprent.EXPRENT_INVOCATION: - type = METHOD_ACCESS_METHOD; + type = MethodAccess.METHOD; break; case Exprent.EXPRENT_ASSIGNMENT: AssignmentExprent asexpr = (AssignmentExprent)exprCore; @@ -131,7 +121,7 @@ public class NestedMemberAccess { if (fexpras.isStatic() || (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) { if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) { - type = METHOD_ACCESS_FIELD_SET; + type = MethodAccess.FIELD_SET; } } } @@ -139,30 +129,31 @@ public class NestedMemberAccess { } } + if (type == MethodAccess.METHOD) { // FIXME: check for private flag of the method - if (type == METHOD_ACCESS_METHOD) { // FIXME: check for private flag of the method - - type = METHOD_ACCESS_NORMAL; + type = MethodAccess.NORMAL; InvocationExprent invexpr = (InvocationExprent)exprCore; - if ((invexpr.isStatic() && invexpr.getLstParameters().size() == parcount) || - (!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR + boolean isStatic = invexpr.isStatic(); + if ((isStatic && invexpr.getLstParameters().size() == parcount) || + (!isStatic && invexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)invexpr.getInstance()).getIndex() == 0 && invexpr.getLstParameters().size() == parcount - 1)) { boolean equalpars = true; + int index = isStatic ? 0 : 1; for (int i = 0; i < invexpr.getLstParameters().size(); i++) { Exprent parexpr = invexpr.getLstParameters().get(i); - if (parexpr.type != Exprent.EXPRENT_VAR || - ((VarExprent)parexpr).getIndex() != i + (invexpr.isStatic() ? 0 : 1)) { + if (parexpr.type != Exprent.EXPRENT_VAR || ((VarExprent)parexpr).getIndex() != index) { equalpars = false; break; } + index += mtdesc.params[i + (isStatic ? 0 : 1)].stackSize; } if (equalpars) { - type = METHOD_ACCESS_METHOD; + type = MethodAccess.METHOD; } } } @@ -188,10 +179,10 @@ public class NestedMemberAccess { if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) { ExitExprent exexpr = (ExitExprent)exprentSecond; - if (exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) { + if (exexpr.getExitType() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) { if (exexpr.getValue().type == Exprent.EXPRENT_VAR && ((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) { - type = METHOD_ACCESS_FIELD_SET; + type = MethodAccess.FIELD_SET; } } } @@ -205,7 +196,7 @@ public class NestedMemberAccess { } } - if (type != METHOD_ACCESS_NORMAL) { + if (type != MethodAccess.NORMAL) { mapMethodType.put(method, type); } else { @@ -220,7 +211,7 @@ public class NestedMemberAccess { return; } - for (MethodWrapper meth : node.wrapper.getMethods()) { + for (MethodWrapper meth : node.getWrapper().getMethods()) { if (meth.root != null) { @@ -228,8 +219,8 @@ public class NestedMemberAccess { DirectGraph graph = meth.getOrBuildGraph(); - HashSet setVisited = new HashSet(); - LinkedList stack = new LinkedList(); + HashSet setVisited = new HashSet<>(); + LinkedList stack = new LinkedList<>(); stack.add(graph.first); while (!stack.isEmpty()) { // TODO: replace with interface iterator? @@ -256,9 +247,7 @@ public class NestedMemberAccess { } } - for (DirectNode ndx : nd.succs) { - stack.add(ndx); - } + stack.addAll(nd.succs); } if (replaced) { @@ -323,12 +312,11 @@ public class NestedMemberAccess { } private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, InvocationExprent invexpr) { - ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(invexpr.getClassname()); MethodWrapper methsource = null; - if (node != null && node.wrapper != null) { - methsource = node.wrapper.getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor()); + if (node != null && node.getWrapper() != null) { + methsource = node.getWrapper().getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor()); } if (methsource == null || !mapMethodType.containsKey(methsource)) { @@ -343,10 +331,10 @@ public class NestedMemberAccess { return null; } - int type = mapMethodType.get(methsource); + MethodAccess type = mapMethodType.get(methsource); - // // FIXME: impossible case. METHOD_ACCESS_NORMAL is not saved in the map - // if(type == METHOD_ACCESS_NORMAL) { + // // FIXME: impossible case. MethodAccess.NORMAL is not saved in the map + // if(type == MethodAccess.NORMAL) { // return null; // } @@ -360,11 +348,11 @@ public class NestedMemberAccess { Exprent retexprent = null; switch (type) { - case METHOD_ACCESS_FIELD_GET: + case FIELD_GET: ExitExprent exsource = (ExitExprent)source; if (exsource.getValue().type == Exprent.EXPRENT_VAR) { // qualified this VarExprent var = (VarExprent)exsource.getValue(); - String varname = methsource.varproc.getVarName(new VarVersionPaar(var)); + String varname = methsource.varproc.getVarName(new VarVersionPair(var)); if (!methdest.setOuterVarNames.contains(varname)) { VarNamesCollector vnc = new VarNamesCollector(); @@ -375,8 +363,8 @@ public class NestedMemberAccess { } int index = methdest.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER); - VarExprent ret = new VarExprent(index, var.getVartype(), methdest.varproc); - methdest.varproc.setVarName(new VarVersionPaar(index, 0), varname); + VarExprent ret = new VarExprent(index, var.getVarType(), methdest.varproc); + methdest.varproc.setVarName(new VarVersionPair(index, 0), varname); retexprent = ret; } @@ -388,7 +376,7 @@ public class NestedMemberAccess { retexprent = ret; } break; - case METHOD_ACCESS_FIELD_SET: + case FIELD_SET: AssignmentExprent ret; if (source.type == Exprent.EXPRENT_EXIT) { ExitExprent extex = (ExitExprent)source; @@ -406,9 +394,17 @@ public class NestedMemberAccess { ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(1)); fexpr.replaceExprent(fexpr.getInstance(), invexpr.getLstParameters().get(0)); } + + // do not use copied bytecodes + ret.getLeft().bytecode = null; + ret.getRight().bytecode = null; + retexprent = ret; break; - case METHOD_ACCESS_METHOD: + case FUNCTION: + retexprent = replaceFunction(invexpr, source); + break; + case METHOD: if (source.type == Exprent.EXPRENT_EXIT) { source = ((ExitExprent)source).getValue(); } @@ -430,6 +426,10 @@ public class NestedMemberAccess { if (retexprent != null) { + // preserve original bytecodes + retexprent.bytecode = null; + retexprent.addBytecodeOffsets(invexpr.bytecode); + // hide synthetic access method boolean hide = true; @@ -440,10 +440,31 @@ public class NestedMemberAccess { } } if (hide) { - node.wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor())); + node.getWrapper().getHiddenMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor())); } } return retexprent; } + + private static Exprent replaceFunction(final InvocationExprent invexpr, final Exprent source) { + FunctionExprent functionExprent = (FunctionExprent)((ExitExprent)source).getValue().copy(); + + List lstParameters = invexpr.getLstParameters(); + + FieldExprent fieldExprent = (FieldExprent)functionExprent.getLstOperands().get(0); + if (fieldExprent.isStatic()) { + if (!lstParameters.isEmpty()) { + return null; + } + return functionExprent; + } + + if (lstParameters.size() != 1) { + return null; + } + + fieldExprent.replaceExprent(fieldExprent.getInstance(), lstParameters.get(0)); + return functionExprent; + } } diff --git a/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java index c7dd8ca..13106f5 100644 --- a/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java @@ -1,23 +1,10 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; import org.jetbrains.java.decompiler.code.Instruction; import org.jetbrains.java.decompiler.code.InstructionSequence; +import org.jetbrains.java.decompiler.code.SimpleInstructionSequence; import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG; @@ -26,12 +13,12 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import java.util.*; -public class DeadCodeHelper { +public final class DeadCodeHelper { public static void removeDeadBlocks(ControlFlowGraph graph) { - LinkedList stack = new LinkedList(); - HashSet setStacked = new HashSet(); + LinkedList stack = new LinkedList<>(); + HashSet setStacked = new HashSet<>(); stack.add(graph.getFirst()); setStacked.add(graph.getFirst()); @@ -39,7 +26,7 @@ public class DeadCodeHelper { while (!stack.isEmpty()) { BasicBlock block = stack.removeFirst(); - List lstSuccs = new ArrayList(block.getSuccs()); + List lstSuccs = new ArrayList<>(block.getSuccs()); lstSuccs.addAll(block.getSuccExceptions()); for (BasicBlock succ : lstSuccs) { @@ -50,7 +37,7 @@ public class DeadCodeHelper { } } - HashSet setAllBlocks = new HashSet(graph.getBlocks()); + HashSet setAllBlocks = new HashSet<>(graph.getBlocks()); setAllBlocks.removeAll(setStacked); for (BasicBlock block : setAllBlocks) { @@ -94,7 +81,7 @@ public class DeadCodeHelper { } } - HashSet setExits = new HashSet(graph.getLast().getPreds()); + HashSet setExits = new HashSet<>(graph.getLast().getPreds()); if (block.getPredExceptions().isEmpty() && (!setExits.contains(block) || block.getPreds().size() == 1)) { @@ -109,15 +96,15 @@ public class DeadCodeHelper { } } - HashSet setPreds = new HashSet(block.getPreds()); - HashSet setSuccs = new HashSet(block.getSuccs()); + HashSet setPreds = new HashSet<>(block.getPreds()); + HashSet setSuccs = new HashSet<>(block.getSuccs()); // collect common exception ranges of predecessors and successors HashSet setCommonExceptionHandlers = null; for (int i = 0; i < 2; ++i) { for (BasicBlock pred : i == 0 ? setPreds : setSuccs) { if (setCommonExceptionHandlers == null) { - setCommonExceptionHandlers = new HashSet(pred.getSuccExceptions()); + setCommonExceptionHandlers = new HashSet<>(pred.getSuccExceptions()); } else { setCommonExceptionHandlers.retainAll(pred.getSuccExceptions()); @@ -159,7 +146,7 @@ public class DeadCodeHelper { BasicBlock pred = block.getPreds().get(0); pred.removeSuccessor(block); - List lstSuccs = new ArrayList(block.getSuccs()); + List lstSuccs = new ArrayList<>(block.getSuccs()); for (BasicBlock succ : lstSuccs) { block.removeSuccessor(succ); pred.addSuccessor(succ); @@ -205,13 +192,13 @@ public class DeadCodeHelper { public static boolean isDominator(ControlFlowGraph graph, BasicBlock block, BasicBlock dom) { - HashSet marked = new HashSet(); + HashSet marked = new HashSet<>(); if (block == dom) { return true; } - LinkedList lstNodes = new LinkedList(); + LinkedList lstNodes = new LinkedList<>(); lstNodes.add(block); while (!lstNodes.isEmpty()) { @@ -230,14 +217,14 @@ public class DeadCodeHelper { for (int i = 0; i < node.getPreds().size(); i++) { BasicBlock pred = node.getPreds().get(i); - if (!marked.contains(pred) && pred != dom) { + if (pred != dom && !marked.contains(pred)) { lstNodes.add(pred); } } for (int i = 0; i < node.getPredExceptions().size(); i++) { BasicBlock pred = node.getPredExceptions().get(i); - if (!marked.contains(pred) && pred != dom) { + if (pred != dom && !marked.contains(pred)) { lstNodes.add(pred); } } @@ -252,7 +239,7 @@ public class DeadCodeHelper { Instruction instr = block.getLastInstruction(); if (instr != null && instr.opcode == CodeConstants.opc_goto) { - block.getSeq().removeInstruction(block.getSeq().length() - 1); + block.getSeq().removeLast(); } } @@ -262,12 +249,161 @@ public class DeadCodeHelper { public static void connectDummyExitBlock(ControlFlowGraph graph) { BasicBlock exit = graph.getLast(); - for (BasicBlock block : new HashSet(exit.getPreds())) { + for (BasicBlock block : new HashSet<>(exit.getPreds())) { exit.removePredecessor(block); block.addSuccessor(exit); } } + public static void extendSynchronizedRangeToMonitorexit(ControlFlowGraph graph) { + + while(true) { + + boolean range_extended = false; + + for (ExceptionRangeCFG range : graph.getExceptions()) { + + Set setPreds = new HashSet<>(); + for (BasicBlock block : range.getProtectedRange()) { + setPreds.addAll(block.getPreds()); + } + for (BasicBlock basicBlock : range.getProtectedRange()) { + setPreds.remove(basicBlock); + } + + if(setPreds.size() != 1) { + continue; // multiple predecessors, obfuscated range + } + + BasicBlock predBlock = setPreds.iterator().next(); + InstructionSequence predSeq = predBlock.getSeq(); + if(predSeq.isEmpty() || predSeq.getLastInstr().opcode != CodeConstants.opc_monitorenter) { + continue; // not a synchronized range + } + + boolean monitorexit_in_range = false; + Set setProtectedBlocks = new HashSet<>(range.getProtectedRange()); + setProtectedBlocks.add(range.getHandler()); + + for (BasicBlock block : setProtectedBlocks) { + InstructionSequence blockSeq = block.getSeq(); + for (int i = 0; i < blockSeq.length(); i++) { + if (blockSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) { + monitorexit_in_range = true; + break; + } + } + if(monitorexit_in_range) { + break; + } + } + + if(monitorexit_in_range) { + continue; // protected range already contains monitorexit + } + + Set setSuccs = new HashSet<>(); + for (BasicBlock block : range.getProtectedRange()) { + setSuccs.addAll(block.getSuccs()); + } + for (BasicBlock basicBlock : range.getProtectedRange()) { + setSuccs.remove(basicBlock); + } + + if(setSuccs.size() != 1) { + continue; // non-unique successor + } + + BasicBlock succBlock = setSuccs.iterator().next(); + InstructionSequence succSeq = succBlock.getSeq(); + + int succ_monitorexit_index = -1; + for (int i = 0; i < succSeq.length(); i++) { + if (succSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) { + succ_monitorexit_index = i; + break; + } + } + + if(succ_monitorexit_index < 0) { + continue; // monitorexit not found in the single successor block + } + + BasicBlock handlerBlock = range.getHandler(); + if(handlerBlock.getSuccs().size() != 1) { + continue; // non-unique handler successor + } + BasicBlock succHandler = handlerBlock.getSuccs().get(0); + InstructionSequence succHandlerSeq = succHandler.getSeq(); + if(succHandlerSeq.isEmpty() || succHandlerSeq.getLastInstr().opcode != CodeConstants.opc_athrow) { + continue; // not a standard synchronized range + } + + int handler_monitorexit_index = -1; + for (int i = 0; i < succHandlerSeq.length(); i++) { + if (succHandlerSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) { + handler_monitorexit_index = i; + break; + } + } + + if(handler_monitorexit_index < 0) { + continue; // monitorexit not found in the handler successor block + } + + // checks successful, prerequisites satisfied, now extend the range + if(succ_monitorexit_index < succSeq.length() - 1) { // split block + + SimpleInstructionSequence seq = new SimpleInstructionSequence(); + for(int counter = 0; counter < succ_monitorexit_index; counter++) { + seq.addInstruction(succSeq.getInstr(0), -1); + succSeq.removeInstruction(0); + } + + // build a separate block + BasicBlock newblock = new BasicBlock(++graph.last_id); + newblock.setSeq(seq); + + // insert new block + for (BasicBlock block : succBlock.getPreds()) { + block.replaceSuccessor(succBlock, newblock); + } + + newblock.addSuccessor(succBlock); + graph.getBlocks().addWithKey(newblock, newblock.id); + + succBlock = newblock; + } + + // copy exception edges and extend protected ranges (successor block) + BasicBlock rangeExitBlock = succBlock.getPreds().get(0); + for (int j = 0; j < rangeExitBlock.getSuccExceptions().size(); j++) { + BasicBlock hd = rangeExitBlock.getSuccExceptions().get(j); + succBlock.addSuccessorException(hd); + + ExceptionRangeCFG rng = graph.getExceptionRange(hd, rangeExitBlock); + rng.getProtectedRange().add(succBlock); + } + + // copy instructions (handler successor block) + InstructionSequence handlerSeq = handlerBlock.getSeq(); + for(int counter = 0; counter < handler_monitorexit_index; counter++) { + handlerSeq.addInstruction(succHandlerSeq.getInstr(0), -1); + succHandlerSeq.removeInstruction(0); + } + + range_extended = true; + break; + } + + if(!range_extended) { + break; + } + } + + } + + public static void incorporateValueReturns(ControlFlowGraph graph) { for (BasicBlock block : graph.getBlocks()) { @@ -311,8 +447,8 @@ public class DeadCodeHelper { if (!block.getPreds().isEmpty()) { - HashSet setPredHandlersUnion = new HashSet(); - HashSet setPredHandlersIntersection = new HashSet(); + HashSet setPredHandlersUnion = new HashSet<>(); + HashSet setPredHandlersIntersection = new HashSet<>(); boolean firstpred = true; for (BasicBlock pred : block.getPreds()) { @@ -328,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) { @@ -339,7 +477,7 @@ public class DeadCodeHelper { } // remove redundant ranges - HashSet setRangesToBeRemoved = new HashSet(block.getSuccExceptions()); + HashSet setRangesToBeRemoved = new HashSet<>(block.getSuccExceptions()); setRangesToBeRemoved.removeAll(setPredHandlersUnion); for (BasicBlock handler : setRangesToBeRemoved) { @@ -369,7 +507,7 @@ public class DeadCodeHelper { } // remove superfluous ranges from successors - for (BasicBlock succ : new HashSet(block.getSuccExceptions())) { + for (BasicBlock succ : new HashSet<>(block.getSuccExceptions())) { if (!bpred.getSuccExceptions().contains(succ)) { ExceptionRangeCFG range = graph.getExceptionRange(succ, block); @@ -416,6 +554,7 @@ public class DeadCodeHelper { if (sameRanges) { seq.addSequence(next.getSeq()); + block.getInstrOldOffsets().addAll(next.getInstrOldOffsets()); next.getSeq().clear(); removeEmptyBlock(graph, next, true); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java new file mode 100644 index 0000000..d6a7ed8 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java @@ -0,0 +1,72 @@ +// 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; +import org.jetbrains.java.decompiler.struct.gen.VarType; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public final class ClasspathHelper { + + private static final Map METHOD_CACHE = Collections.synchronizedMap(new HashMap<>()); + + public static Method findMethod(String classname, String methodName, MethodDescriptor descriptor) { + String targetClass = classname.replace('/', '.'); + String methodSignature = buildMethodSignature(targetClass + '.' + methodName, descriptor); + + Method method; + if (METHOD_CACHE.containsKey(methodSignature)) { + method = METHOD_CACHE.get(methodSignature); + } + else { + method = findMethodOnClasspath(targetClass, methodSignature); + METHOD_CACHE.put(methodSignature, method); + } + + return method; + } + + private static Method findMethodOnClasspath(String targetClass, String methodSignature) { + try { + // use bootstrap classloader to only provide access to JRE classes + Class cls = new ClassLoader(null) {}.loadClass(targetClass); + for (Method mtd : cls.getMethods()) { + // use contains() to ignore access modifiers and thrown exceptions + if (mtd.toString().contains(methodSignature)) { + return mtd; + } + } + } + catch (Exception e) { + // ignore + } + return null; + } + + private static String buildMethodSignature(String name, MethodDescriptor md) { + StringBuilder sb = new StringBuilder(); + + appendType(sb, md.ret); + sb.append(' ').append(name).append('('); + for (VarType param : md.params) { + appendType(sb, param); + sb.append(','); + } + if (sb.charAt(sb.length() - 1) == ',') { + sb.setLength(sb.length() - 1); + } + sb.append(')'); + + return sb.toString(); + } + + private static void appendType(StringBuilder sb, VarType type) { + sb.append(type.value.replace('/', '.')); + for (int i = 0; i < type.arrayDim; i++) { + sb.append("[]"); + } + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java index c6495e6..14576cc 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -21,11 +7,11 @@ 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) { - LinkedList stack = new LinkedList(); + LinkedList stack = new LinkedList<>(); stack.add(root); while (!stack.isEmpty()) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java index 1b641a6..3b255be 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java @@ -1,29 +1,19 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; import org.jetbrains.java.decompiler.modules.decompiler.exps.*; +import org.jetbrains.java.decompiler.struct.consts.PooledConstant; +import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Set; -public class ConcatenationHelper { +public final class ConcatenationHelper { private static final String builderClass = "java/lang/StringBuilder"; private static final String bufferClass = "java/lang/StringBuffer"; @@ -52,6 +42,12 @@ public class ConcatenationHelper { exprTmp = iex.getInstance(); } } + else if ("makeConcatWithConstants".equals(iex.getName())) { // java 9 style + List parameters = extractParameters(iex.getBootstrapArguments(), iex); + if (parameters.size() >= 2) { + return createConcatExprent(parameters, expr.bytecode); + } + } } if (exprTmp == null) { @@ -60,7 +56,7 @@ public class ConcatenationHelper { // iterate in depth, collecting possible operands - List lstOperands = new ArrayList(); + List lstOperands = new ArrayList<>(); while (true) { @@ -104,7 +100,7 @@ public class ConcatenationHelper { } if (first2str == 0) { - lstOperands.add(0, new ConstExprent(VarType.VARTYPE_STRING, "")); + lstOperands.add(0, new ConstExprent(VarType.VARTYPE_STRING, "", expr.bytecode)); } // remove redundant String.valueOf @@ -125,20 +121,69 @@ public class ConcatenationHelper { lstOperands.set(i, rep); } } + return createConcatExprent(lstOperands, expr.bytecode); + } + private static Exprent createConcatExprent(List lstOperands, Set bytecode) { // build exprent to return Exprent func = lstOperands.get(0); for (int i = 1; i < lstOperands.size(); i++) { - List lstTmp = new ArrayList(); - lstTmp.add(func); - lstTmp.add(lstOperands.get(i)); - func = new FunctionExprent(FunctionExprent.FUNCTION_STRCONCAT, lstTmp); + func = new FunctionExprent(FunctionExprent.FUNCTION_STR_CONCAT, Arrays.asList(func, lstOperands.get(i)), bytecode); } return func; } + // See StringConcatFactory in jdk sources + private static final char TAG_ARG = '\u0001'; + private static final char TAG_CONST = '\u0002'; + + private static List extractParameters(List bootstrapArguments, InvocationExprent expr) { + List parameters = expr.getLstParameters(); + if (bootstrapArguments != null) { + PooledConstant constant = bootstrapArguments.get(0); + if (constant.type == CodeConstants.CONSTANT_String) { + String recipe = ((PrimitiveConstant)constant).getString(); + + List res = new ArrayList<>(); + StringBuilder acc = new StringBuilder(); + int parameterId = 0; + for (int i = 0; i < recipe.length(); i++) { + char c = recipe.charAt(i); + + if (c == TAG_CONST || c == TAG_ARG) { + // Detected a special tag, flush all accumulated characters + // as a constant first: + if (acc.length() > 0) { + res.add(new ConstExprent(VarType.VARTYPE_STRING, acc.toString(), expr.bytecode)); + acc.setLength(0); + } + if (c == TAG_CONST) { + // skip for now + } + if (c == TAG_ARG) { + res.add(parameters.get(parameterId++)); + } + } + else { + // Not a special characters, this is a constant embedded into + // the recipe itself. + acc.append(c); + } + } + + // Flush the remaining characters as constant: + if (acc.length() > 0) { + res.add(new ConstExprent(VarType.VARTYPE_STRING, acc.toString(), expr.bytecode)); + } + + return res; + } + } + return new ArrayList<>(parameters); + } + private static boolean isAppendConcat(InvocationExprent expr, VarType cltype) { if ("append".equals(expr.getName())) { @@ -167,13 +212,9 @@ public class ConcatenationHelper { } private static boolean isNewConcat(NewExprent expr, VarType cltype) { - - if (expr.getNewtype().equals(cltype)) { + if (expr.getNewType().equals(cltype)) { VarType[] params = expr.getConstructor().getDescriptor().params; - if (params.length == 0 || (params.length == 1 && - params[0].equals(VarType.VARTYPE_STRING))) { - return true; - } + return params.length == 0 || params.length == 1 && params[0].equals(VarType.VARTYPE_STRING); } return false; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java index 9c719db..50613fc 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java @@ -1,33 +1,22 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; -public class DecHelper { +public final class DecHelper { - public static boolean checkStatementExceptions(List lst) { + public static boolean checkStatementExceptions(List lst) { - Set all = new HashSet(lst); + Set all = new HashSet<>(lst); - Set handlers = new HashSet(); + Set handlers = new HashSet<>(); Set intersection = null; for (Statement stat : lst) { @@ -37,7 +26,7 @@ public class DecHelper { intersection = setNew; } else { - HashSet interclone = new HashSet(intersection); + HashSet interclone = new HashSet<>(intersection); interclone.removeAll(setNew); intersection.retainAll(setNew); @@ -66,7 +55,7 @@ public class DecHelper { return true; } - public static boolean isChoiceStatement(Statement head, List lst) { + public static boolean isChoiceStatement(Statement head, List lst) { Statement post = null; @@ -159,7 +148,7 @@ public class DecHelper { if (head == statd) { return false; } - if (!setDest.contains(statd) && post != statd) { + if (post != statd && !setDest.contains(statd)) { if (post != null) { return false; } @@ -194,25 +183,17 @@ public class DecHelper { return true; } - - public static HashSet getUniquePredExceptions(Statement head) { - - HashSet setHandlers = new HashSet(head.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD)); - - Iterator it = setHandlers.iterator(); - while (it.hasNext()) { - if (it.next().getPredecessorEdges(StatEdge.TYPE_EXCEPTION).size() > 1) { - it.remove(); - } - } + public static Set getUniquePredExceptions(Statement head) { + Set setHandlers = new HashSet<>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD)); + setHandlers.removeIf(statement -> statement.getPredecessorEdges(StatEdge.TYPE_EXCEPTION).size() > 1); return setHandlers; } - public static List copyExprentList(List lst) { - List ret = new ArrayList(); + public static List copyExprentList(List lst) { + List ret = new ArrayList<>(); for (Exprent expr : lst) { ret.add(expr.copy()); } return ret; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java index 8c5dd4c..7ef6d2f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -30,12 +16,12 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.util.*; -public class DomHelper { +public final class DomHelper { private static RootStatement graphToStatement(ControlFlowGraph graph) { - VBStyleCollection stats = new VBStyleCollection(); + VBStyleCollection stats = new VBStyleCollection<>(); VBStyleCollection blocks = graph.getBlocks(); for (BasicBlock block : blocks) { @@ -46,8 +32,7 @@ public class DomHelper { // head statement Statement firstst = stats.getWithKey(firstblock.id); // dummy exit statement - Statement dummyexit = new Statement(); - dummyexit.type = Statement.TYPE_DUMMYEXIT; + DummyExitStatement dummyexit = new DummyExitStatement(); Statement general; if (stats.size() > 1 || firstblock.isSuccessor(firstblock)) { // multiple basic blocks or an infinite loop of one block @@ -104,14 +89,14 @@ public class DomHelper { public static VBStyleCollection, Integer> calcPostDominators(Statement container) { - HashMap> lists = new HashMap>(); + HashMap> lists = new HashMap<>(); StrongConnectivityHelper schelper = new StrongConnectivityHelper(container); List> components = schelper.getComponents(); List lstStats = container.getPostReversePostOrderList(StrongConnectivityHelper.getExitReps(components)); - FastFixedSetFactory factory = new FastFixedSetFactory(lstStats); + FastFixedSetFactory factory = new FastFixedSetFactory<>(lstStats); FastFixedSet setFlagNodes = factory.spawnEmptySet(); setFlagNodes.setAllElements(); @@ -177,26 +162,22 @@ public class DomHelper { } while (!setFlagNodes.isEmpty()); - VBStyleCollection, Integer> ret = new VBStyleCollection, Integer>(); + VBStyleCollection, Integer> ret = new VBStyleCollection<>(); List lstRevPost = container.getReversePostOrderList(); // sort order crucial! - final HashMap mapSortOrder = new HashMap(); + final HashMap mapSortOrder = new HashMap<>(); for (int i = 0; i < lstRevPost.size(); i++) { mapSortOrder.put(lstRevPost.get(i).id, i); } for (Statement st : lstStats) { - List lstPosts = new ArrayList(); + List lstPosts = new ArrayList<>(); for (Statement stt : lists.get(st)) { lstPosts.add(stt.id); } - Collections.sort(lstPosts, new Comparator() { - public int compare(Integer o1, Integer o2) { - return mapSortOrder.get(o1).compareTo(mapSortOrder.get(o2)); - } - }); + lstPosts.sort(Comparator.comparing(mapSortOrder::get)); if (lstPosts.size() > 1 && lstPosts.get(0).intValue() == st.id) { lstPosts.add(lstPosts.remove(0)); @@ -212,11 +193,17 @@ public class DomHelper { RootStatement root = graphToStatement(graph); - if (!processStatement(root, new HashMap>())) { + if (!processStatement(root, new HashMap<>())) { + + // try { + // DotExporter.toDotFile(root.getFirst().getStats().get(13), new File("c:\\Temp\\stat1.dot")); + // } catch (Exception ex) { + // ex.printStackTrace(); + // } throw new RuntimeException("parsing failure!"); } - LabelHelper.lowContinueLabels(root, new HashSet()); + LabelHelper.lowContinueLabels(root, new HashSet<>()); SequenceHelper.condenseSequences(root); root.buildMonitorFlags(); @@ -286,7 +273,7 @@ public class DomHelper { SynchronizedStatement sync = new SynchronizedStatement(current, ca.getFirst(), ca.getHandler()); sync.setAllParent(); - for (StatEdge edge : new HashSet(ca.getLabelEdges())) { + for (StatEdge edge : new HashSet<>(ca.getLabelEdges())) { sync.addLabeledEdge(edge); } @@ -356,7 +343,7 @@ public class DomHelper { // DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot")); // } catch(Exception ex) {ex.printStackTrace();} - mapExtPost = new HashMap>(); + mapExtPost = new HashMap<>(); mapRefreshed = true; } @@ -377,7 +364,7 @@ public class DomHelper { Statement stat = findGeneralStatement(general, forceall, mapExtPost); if (stat != null) { - boolean complete = processStatement(stat, general.getFirst() == stat ? mapExtPost : new HashMap>()); + boolean complete = processStatement(stat, general.getFirst() == stat ? mapExtPost : new HashMap<>()); if (complete) { // replace general purpose statement with simple one @@ -387,7 +374,7 @@ public class DomHelper { return false; } - mapExtPost = new HashMap>(); + mapExtPost = new HashMap<>(); mapRefreshed = true; reducibility = 0; } @@ -408,7 +395,7 @@ public class DomHelper { break; } else { - mapExtPost = new HashMap>(); + mapExtPost = new HashMap<>(); } } @@ -426,13 +413,13 @@ public class DomHelper { } if (forceall) { - vbPost = new VBStyleCollection, Integer>(); + vbPost = new VBStyleCollection<>(); List lstAll = stat.getPostReversePostOrderList(); for (Statement st : lstAll) { Set set = mapExtPost.get(st.id); if (set != null) { - vbPost.addWithKey(new ArrayList(set), st.id); // FIXME: sort order!! + vbPost.addWithKey(new ArrayList<>(set), st.id); // FIXME: sort order!! } } @@ -442,7 +429,7 @@ public class DomHelper { for (Integer id : setFirst) { List lst = vbPost.getWithKey(id); if (lst == null) { - vbPost.addWithKey(lst = new ArrayList(), id); + vbPost.addWithKey(lst = new ArrayList<>(), id); } lst.add(id); } @@ -466,14 +453,12 @@ public class DomHelper { Set setExtPosts = mapExtPost.get(headid); - for (int i = 0; i < posts.size(); i++) { - - Integer postid = posts.get(i); - if (!postid.equals(headid) && !setExtPosts.contains(postid)) { + for (Integer postId : posts) { + if (!postId.equals(headid) && !setExtPosts.contains(postId)) { continue; } - Statement post = stats.getWithKey(postid); + Statement post = stats.getWithKey(postId); if (post == null) { // possible in case of an inherited postdominance set continue; @@ -481,11 +466,11 @@ public class DomHelper { boolean same = (post == head); - HashSet setNodes = new HashSet(); - HashSet setPreds = new HashSet(); + HashSet setNodes = new HashSet<>(); + HashSet setPreds = new HashSet<>(); // collect statement nodes - HashSet setHandlers = new HashSet(); + HashSet setHandlers = new HashSet<>(); setHandlers.add(head); while (true) { @@ -503,7 +488,7 @@ public class DomHelper { } if (addhd) { - LinkedList lstStack = new LinkedList(); + LinkedList lstStack = new LinkedList<>(); lstStack.add(handler); while (!lstStack.isEmpty()) { @@ -552,14 +537,14 @@ public class DomHelper { // build statement and return if (excok) { - Statement res = null; + Statement res; setPreds.removeAll(setNodes); if (setPreds.size() == 0) { if ((setNodes.size() > 1 || head.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).contains(head)) && setNodes.size() < stats.size()) { - if (checkSynchronizedCompleteness(head, setNodes)) { + if (checkSynchronizedCompleteness(setNodes)) { res = new GeneralStatement(head, setNodes, same ? null : post); stat.collapseNodesToStatement(res); @@ -574,8 +559,7 @@ public class DomHelper { return null; } - private static boolean checkSynchronizedCompleteness(Statement head, HashSet setNodes) { - + private static boolean checkSynchronizedCompleteness(Set setNodes) { // check exit nodes for (Statement stat : setNodes) { if (stat.isMonitorEnter()) { @@ -617,26 +601,21 @@ public class DomHelper { // update the postdominator map if (!mapExtPost.isEmpty()) { - HashSet setOldNodes = new HashSet(); + HashSet setOldNodes = new HashSet<>(); for (Statement old : result.getStats()) { setOldNodes.add(old.id); } Integer newid = result.id; - for (Integer key : new ArrayList(mapExtPost.keySet())) { + for (Integer key : new ArrayList<>(mapExtPost.keySet())) { Set set = mapExtPost.get(key); int oldsize = set.size(); set.removeAll(setOldNodes); if (setOldNodes.contains(key)) { - Set setNew = mapExtPost.get(newid); - if (setNew == null) { - mapExtPost.put(newid, setNew = new HashSet()); - } - setNew.addAll(set); - + mapExtPost.computeIfAbsent(newid, k -> new HashSet<>()).addAll(set); mapExtPost.remove(key); } else { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java deleted file mode 100644 index 16f7da0..0000000 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.modules.decompiler; - -import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - - -public class EliminateLoopsHelper { - - - // public static boolean eliminateLoops(Statement root) { - // - // boolean ret = eliminateLoopsRec(root); - // - // if(ret) { - // SequenceHelper.condenseSequences(root); - // - // HashSet setReorderedIfs = new HashSet(); - // - // SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(false); - // while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, null)) { - // SequenceHelper.condenseSequences(root); - // } - // } - // - // return ret; - // } - - private static boolean eliminateLoopsRec(Statement stat) { - - for (Statement st : stat.getStats()) { - if (eliminateLoopsRec(st)) { - return true; - } - } - - if (stat.type == Statement.TYPE_DO && isLoopRedundant((DoStatement)stat)) { - return true; - } - - return false; - } - - private static boolean isLoopRedundant(DoStatement loop) { - - if (loop.getLooptype() != DoStatement.LOOP_DO) { - return false; - } - - // get parent loop if exists - Statement parentloop = loop.getParent(); - while (parentloop != null && parentloop.type != Statement.TYPE_DO) { - parentloop = parentloop.getParent(); - } - - if (parentloop == null || parentloop.getBasichead() != loop.getBasichead()) { - return false; - } - - // collect relevant break edges - List lstBreakEdges = new ArrayList(); - for (StatEdge edge : loop.getLabelEdges()) { - if (edge.getType() == StatEdge.TYPE_BREAK) { // all break edges are explicit because of LOOP_DO type - lstBreakEdges.add(edge); - } - } - - - Statement loopcontent = loop.getFirst(); - - boolean firstok = loopcontent.getAllSuccessorEdges().isEmpty(); - if (!firstok) { - StatEdge edge = loopcontent.getAllSuccessorEdges().get(0); - firstok = (edge.closure == loop && edge.getType() == StatEdge.TYPE_BREAK); - if (firstok) { - lstBreakEdges.remove(edge); - } - } - - - if (!lstBreakEdges.isEmpty()) { - if (firstok) { - - HashMap statLabeled = new HashMap(); - List lstEdgeClosures = new ArrayList(); - - for (StatEdge edge : lstBreakEdges) { - Statement minclosure = LowBreakHelper.getMinClosure(loopcontent, edge.getSource()); - lstEdgeClosures.add(minclosure); - } - - int precount = loop.isLabeled() ? 1 : 0; - for (Statement st : lstEdgeClosures) { - if (!statLabeled.containsKey(st.id)) { - boolean btemp = st.isLabeled(); - precount += btemp ? 1 : 0; - statLabeled.put(st.id, btemp); - } - } - - for (int i = 0; i < lstBreakEdges.size(); i++) { - Statement st = lstEdgeClosures.get(i); - statLabeled.put(st.id, LowBreakHelper.isBreakEdgeLabeled(lstBreakEdges.get(i).getSource(), st) | statLabeled.get(st.id)); - } - - for (int i = 0; i < lstBreakEdges.size(); i++) { - lstEdgeClosures.set(i, getMaxBreakLift(lstEdgeClosures.get(i), lstBreakEdges.get(i), statLabeled, loop)); - } - - statLabeled.clear(); - for (Statement st : lstEdgeClosures) { - statLabeled.put(st.id, st.isLabeled()); - } - - for (int i = 0; i < lstBreakEdges.size(); i++) { - Statement st = lstEdgeClosures.get(i); - statLabeled.put(st.id, LowBreakHelper.isBreakEdgeLabeled(lstBreakEdges.get(i).getSource(), st) | statLabeled.get(st.id)); - } - - int postcount = 0; - for (Boolean val : statLabeled.values()) { - postcount += val ? 1 : 0; - } - - if (precount <= postcount) { - return false; - } - else { - for (int i = 0; i < lstBreakEdges.size(); i++) { - lstEdgeClosures.get(i).addLabeledEdge(lstBreakEdges.get(i)); - } - } - } - else { - return false; - } - } - - eliminateLoop(loop, parentloop); - - return true; - } - - private static Statement getMaxBreakLift(Statement stat, StatEdge edge, HashMap statLabeled, Statement max) { - - Statement closure = stat; - Statement newclosure = stat; - - while ((newclosure = getNextBreakLift(newclosure, edge, statLabeled, max)) != null) { - closure = newclosure; - } - - return closure; - } - - private static Statement getNextBreakLift(Statement stat, StatEdge edge, HashMap statLabeled, Statement max) { - - Statement closure = stat.getParent(); - - while (closure != null && closure != max && !closure.containsStatementStrict(edge.getDestination())) { - - boolean edge_labeled = LowBreakHelper.isBreakEdgeLabeled(edge.getSource(), closure); - boolean stat_labeled = statLabeled.containsKey(closure.id) ? statLabeled.get(closure.id) : closure.isLabeled(); - - if (stat_labeled || !edge_labeled) { - return closure; - } - - closure = closure.getParent(); - } - - return null; - } - - private static void eliminateLoop(Statement loop, Statement parentloop) { - - // move continue edges to the parent loop - List lst = new ArrayList(loop.getLabelEdges()); - for (StatEdge edge : lst) { - loop.removePredecessor(edge); - edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, parentloop); - parentloop.addPredecessor(edge); - - parentloop.addLabeledEdge(edge); - } - - // remove the last break edge, if exists - Statement loopcontent = loop.getFirst(); - if (!loopcontent.getAllSuccessorEdges().isEmpty()) { - loopcontent.removeSuccessor(loopcontent.getAllSuccessorEdges().get(0)); - } - - // replace loop with its content - loop.getParent().replaceStatement(loop, loopcontent); - } -} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java index af73584..8e0ac9c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -21,40 +7,30 @@ import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.*; -import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import java.util.ArrayList; import java.util.Arrays; 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); if (changed > 0) { - cleanUpUnreachableBlocks(root); - SequenceHelper.condenseSequences(root); } return (changed > 0); } - private static void cleanUpUnreachableBlocks(Statement stat) { - boolean found; do { - found = false; for (int i = 0; i < stat.getStats().size(); i++) { - Statement st = stat.getStats().get(i); cleanUpUnreachableBlocks(st); @@ -71,7 +47,7 @@ public class ExitHelper { set.remove(secondlast); if (set.isEmpty()) { - last.setExprents(new ArrayList()); + last.setExprents(new ArrayList<>()); found = true; break; } @@ -83,16 +59,12 @@ public class ExitHelper { while (found); } - private static int integrateExits(Statement stat) { - int ret = 0; - Statement dest = null; + Statement dest; if (stat.getExprents() == null) { - while (true) { - int changed = 0; for (Statement st : stat.getStats()) { @@ -108,33 +80,31 @@ public class ExitHelper { } } + if (stat.type == Statement.TYPE_IF) { + IfStatement ifst = (IfStatement)stat; + if (ifst.getIfstat() == null) { + StatEdge ifedge = ifst.getIfEdge(); + dest = isExitEdge(ifedge); + if (dest != null) { + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + bstat.setExprents(DecHelper.copyExprentList(dest.getExprents())); - switch (stat.type) { - case Statement.TYPE_IF: - IfStatement ifst = (IfStatement)stat; - if (ifst.getIfstat() == null) { - StatEdge ifedge = ifst.getIfEdge(); - dest = isExitEdge(ifedge); - if (dest != null) { - BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( - DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - bstat.setExprents(DecHelper.copyExprentList(dest.getExprents())); + ifst.getFirst().removeSuccessor(ifedge); + StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, ifst.getFirst(), bstat); + ifst.getFirst().addSuccessor(newedge); + ifst.setIfEdge(newedge); + ifst.setIfstat(bstat); + ifst.getStats().addWithKey(bstat, bstat.id); + bstat.setParent(ifst); - ifst.getFirst().removeSuccessor(ifedge); - StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, ifst.getFirst(), bstat); - ifst.getFirst().addSuccessor(newedge); - ifst.setIfEdge(newedge); - ifst.setIfstat(bstat); - ifst.getStats().addWithKey(bstat, bstat.id); - bstat.setParent(ifst); - - StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0); - StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination()); - bstat.addSuccessor(newexitedge); - oldexitedge.closure.addLabeledEdge(newexitedge); - ret = 1; - } + StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0); + StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination()); + bstat.addSuccessor(newexitedge); + oldexitedge.closure.addLabeledEdge(newexitedge); + ret = 1; } + } } } @@ -168,15 +138,12 @@ public class ExitHelper { // LabelHelper.lowContinueLabels(block, new HashSet()); // do it by hand for (StatEdge prededge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) { - block.removePredecessor(prededge); prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, stat); stat.addPredecessor(prededge); - stat.addLabeledEdge(prededge); } - stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, bstat)); for (StatEdge edge : dest.getAllPredecessorEdges()) { @@ -202,11 +169,9 @@ public class ExitHelper { } private static Statement isExitEdge(StatEdge edge) { - Statement dest = edge.getDestination(); - if (edge.getType() == StatEdge.TYPE_BREAK && dest.type == Statement.TYPE_BASICBLOCK - && edge.explicit && (edge.labeled || isOnlyEdge(edge))) { + if (edge.getType() == StatEdge.TYPE_BREAK && dest.type == Statement.TYPE_BASICBLOCK && edge.explicit && (edge.labeled || isOnlyEdge(edge))) { List data = dest.getExprents(); if (data != null && data.size() == 1) { @@ -220,7 +185,6 @@ public class ExitHelper { } private static boolean isOnlyEdge(StatEdge edge) { - Statement stat = edge.getDestination(); for (StatEdge ed : stat.getAllPredecessorEdges()) { @@ -243,11 +207,10 @@ public class ExitHelper { return true; } - public static boolean removeRedundantReturns(RootStatement root) { + public static void removeRedundantReturns(RootStatement root) { + DummyExitStatement dummyExit = root.getDummyExit(); - boolean res = false; - - for (StatEdge edge : root.getDummyExit().getAllPredecessorEdges()) { + for (StatEdge edge : dummyExit.getAllPredecessorEdges()) { if (!edge.explicit) { Statement source = edge.getSource(); List lstExpr = source.getExprents(); @@ -255,91 +218,14 @@ public class ExitHelper { Exprent expr = lstExpr.get(lstExpr.size() - 1); if (expr.type == Exprent.EXPRENT_EXIT) { ExitExprent ex = (ExitExprent)expr; - if (ex.getExittype() == ExitExprent.EXIT_RETURN && ex.getValue() == null) { + if (ex.getExitType() == ExitExprent.EXIT_RETURN && ex.getValue() == null) { // remove redundant return + dummyExit.addBytecodeOffsets(ex.bytecode); lstExpr.remove(lstExpr.size() - 1); - res = true; } } } } } - - return res; } - - public static boolean handleReturnFromInitializer(RootStatement root) { - - boolean res = false; - - Statement exit = root.getDummyExit(); - Statement top = root.getFirst(); - Statement newret = null; - - boolean sharedcreated = false; - - for (StatEdge edge : exit.getAllPredecessorEdges()) { - if (edge.explicit) { - - if (!sharedcreated) { - newret = addSharedInitializerReturn(root); - sharedcreated = true; - } - - Statement source = edge.getSource(); - List lstExpr = source.getExprents(); - if (lstExpr != null && !lstExpr.isEmpty()) { - Exprent expr = lstExpr.get(lstExpr.size() - 1); - if (expr.type == Exprent.EXPRENT_EXIT) { - ExitExprent ex = (ExitExprent)expr; - if (ex.getExittype() == ExitExprent.EXIT_RETURN && ex.getValue() == null) { - lstExpr.remove(lstExpr.size() - 1); - - source.removeSuccessor(edge); - source.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, source, newret, top)); - - res = true; - } - } - } - } - } - - return res; - } - - private static Statement addSharedInitializerReturn(RootStatement root) { - - Statement exit = root.getDummyExit(); - Statement top = root.getFirst(); - - // build a new statement with the single instruction 'return' - BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( - DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - - ExitExprent retexpr = new ExitExprent(ExitExprent.EXIT_RETURN, null, - ((MethodDescriptor)DecompilerContext - .getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR)).ret); - // a changeable list needed - bstat.setExprents(new ArrayList(Arrays.asList(new Exprent[]{retexpr}))); - - // build sequence to replace the former top statement - SequenceStatement seq = new SequenceStatement(Arrays.asList(top, bstat)); - top.setParent(seq); - bstat.setParent(seq); - seq.setParent(root); - - root.getStats().removeWithKey(top.id); - root.getStats().addWithKeyAndIndex(0, seq, seq.id); - root.setFirst(seq); - - for (StatEdge succedge : top.getAllSuccessorEdges()) { - top.removeSuccessor(succedge); - } - - top.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, top, bstat)); - bstat.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, bstat, exit, seq)); - - return bstat; - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java index 2450475..be43b4c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -20,7 +6,7 @@ import org.jetbrains.java.decompiler.code.Instruction; import org.jetbrains.java.decompiler.code.InstructionSequence; import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.TextBuffer; +import org.jetbrains.java.decompiler.util.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; @@ -38,152 +24,115 @@ import org.jetbrains.java.decompiler.struct.consts.PooledConstant; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; -import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextUtil; import java.util.*; -import org.jetbrains.java.decompiler.util.SortUtil; -import org.jetbrains.java.decompiler.util.Util; public class ExprProcessor implements CodeConstants { - public static final String UNDEFINED_TYPE_STRING = ""; public static final String UNKNOWN_TYPE_STRING = ""; public static final String NULL_TYPE_STRING = ""; - private static final HashMap mapConsts = new HashMap(); - + private static final Map mapConsts = new HashMap<>(); static { - - // mapConsts.put(new Integer(opc_i2l), new - // Integer(FunctionExprent.FUNCTION_I2L)); - // mapConsts.put(new Integer(opc_i2f), new - // Integer(FunctionExprent.FUNCTION_I2F)); - // mapConsts.put(new Integer(opc_i2d), new - // Integer(FunctionExprent.FUNCTION_I2D)); - // mapConsts.put(new Integer(opc_l2i), new - // Integer(FunctionExprent.FUNCTION_L2I)); - // mapConsts.put(new Integer(opc_l2f), new - // Integer(FunctionExprent.FUNCTION_L2F)); - // mapConsts.put(new Integer(opc_l2d), new - // Integer(FunctionExprent.FUNCTION_L2D)); - // mapConsts.put(new Integer(opc_f2i), new - // Integer(FunctionExprent.FUNCTION_F2I)); - // mapConsts.put(new Integer(opc_f2l), new - // Integer(FunctionExprent.FUNCTION_F2L)); - // mapConsts.put(new Integer(opc_f2d), new - // Integer(FunctionExprent.FUNCTION_F2D)); - // mapConsts.put(new Integer(opc_d2i), new - // Integer(FunctionExprent.FUNCTION_D2I)); - // mapConsts.put(new Integer(opc_d2l), new - // Integer(FunctionExprent.FUNCTION_D2L)); - // mapConsts.put(new Integer(opc_d2f), new - // Integer(FunctionExprent.FUNCTION_D2F)); - // mapConsts.put(new Integer(opc_i2b), new - // Integer(FunctionExprent.FUNCTION_I2B)); - // mapConsts.put(new Integer(opc_i2c), new - // Integer(FunctionExprent.FUNCTION_I2C)); - // mapConsts.put(new Integer(opc_i2s), new - // Integer(FunctionExprent.FUNCTION_I2S)); - - mapConsts.put(new Integer(opc_arraylength), new Integer(FunctionExprent.FUNCTION_ARRAYLENGTH)); - mapConsts.put(new Integer(opc_checkcast), new Integer(FunctionExprent.FUNCTION_CAST)); - mapConsts.put(new Integer(opc_instanceof), new Integer(FunctionExprent.FUNCTION_INSTANCEOF)); + mapConsts.put(opc_arraylength, FunctionExprent.FUNCTION_ARRAY_LENGTH); + mapConsts.put(opc_checkcast, FunctionExprent.FUNCTION_CAST); + mapConsts.put(opc_instanceof, FunctionExprent.FUNCTION_INSTANCEOF); } - private static final VarType[] consts = - new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_CLASS, - VarType.VARTYPE_STRING}; + private static final VarType[] consts = { + VarType.VARTYPE_INT, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_CLASS, VarType.VARTYPE_STRING + }; - private static final VarType[] vartypes = - new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT}; + private static final VarType[] varTypes = { + VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT + }; - private static final VarType[] arrtypes = - new VarType[]{VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT, - VarType.VARTYPE_BOOLEAN, VarType.VARTYPE_CHAR, VarType.VARTYPE_SHORT}; + private static final VarType[] arrTypes = { + VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT, + VarType.VARTYPE_BOOLEAN, VarType.VARTYPE_CHAR, VarType.VARTYPE_SHORT + }; - private static final int[] func1 = - new int[]{FunctionExprent.FUNCTION_ADD, FunctionExprent.FUNCTION_SUB, FunctionExprent.FUNCTION_MUL, FunctionExprent.FUNCTION_DIV, - FunctionExprent.FUNCTION_REM}; + private static final int[] func1 = { + FunctionExprent.FUNCTION_ADD, FunctionExprent.FUNCTION_SUB, FunctionExprent.FUNCTION_MUL, FunctionExprent.FUNCTION_DIV, + FunctionExprent.FUNCTION_REM + }; + private static final int[] func2 = { + FunctionExprent.FUNCTION_SHL, FunctionExprent.FUNCTION_SHR, FunctionExprent.FUNCTION_USHR, FunctionExprent.FUNCTION_AND, + FunctionExprent.FUNCTION_OR, FunctionExprent.FUNCTION_XOR + }; + private static final int[] func3 = { + FunctionExprent.FUNCTION_I2L, FunctionExprent.FUNCTION_I2F, FunctionExprent.FUNCTION_I2D, FunctionExprent.FUNCTION_L2I, + FunctionExprent.FUNCTION_L2F, FunctionExprent.FUNCTION_L2D, FunctionExprent.FUNCTION_F2I, FunctionExprent.FUNCTION_F2L, + FunctionExprent.FUNCTION_F2D, FunctionExprent.FUNCTION_D2I, FunctionExprent.FUNCTION_D2L, FunctionExprent.FUNCTION_D2F, + FunctionExprent.FUNCTION_I2B, FunctionExprent.FUNCTION_I2C, FunctionExprent.FUNCTION_I2S + }; + private static final int[] func4 = { + FunctionExprent.FUNCTION_LCMP, FunctionExprent.FUNCTION_FCMPL, FunctionExprent.FUNCTION_FCMPG, FunctionExprent.FUNCTION_DCMPL, + FunctionExprent.FUNCTION_DCMPG + }; + private static final int[] func5 = { + IfExprent.IF_EQ, IfExprent.IF_NE, IfExprent.IF_LT, IfExprent.IF_GE, IfExprent.IF_GT, IfExprent.IF_LE + }; + private static final int[] func6 = { + IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPGT, IfExprent.IF_ICMPLE, + IfExprent.IF_ACMPEQ, IfExprent.IF_ACMPNE + }; + private static final int[] func7 = {IfExprent.IF_NULL, IfExprent.IF_NONNULL}; + private static final int[] func8 = {MonitorExprent.MONITOR_ENTER, MonitorExprent.MONITOR_EXIT}; - private static final int[] func2 = - new int[]{FunctionExprent.FUNCTION_SHL, FunctionExprent.FUNCTION_SHR, FunctionExprent.FUNCTION_USHR, FunctionExprent.FUNCTION_AND, - FunctionExprent.FUNCTION_OR, FunctionExprent.FUNCTION_XOR}; + private static final int[] arrTypeIds = { + CodeConstants.TYPE_BOOLEAN, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_DOUBLE, + CodeConstants.TYPE_BYTE, CodeConstants.TYPE_SHORT, CodeConstants.TYPE_INT, CodeConstants.TYPE_LONG + }; - private static final int[] func3 = - new int[]{FunctionExprent.FUNCTION_I2L, FunctionExprent.FUNCTION_I2F, FunctionExprent.FUNCTION_I2D, FunctionExprent.FUNCTION_L2I, - FunctionExprent.FUNCTION_L2F, FunctionExprent.FUNCTION_L2D, FunctionExprent.FUNCTION_F2I, FunctionExprent.FUNCTION_F2L, - FunctionExprent.FUNCTION_F2D, - FunctionExprent.FUNCTION_D2I, FunctionExprent.FUNCTION_D2L, FunctionExprent.FUNCTION_D2F, FunctionExprent.FUNCTION_I2B, - FunctionExprent.FUNCTION_I2C, - FunctionExprent.FUNCTION_I2S}; + private static final int[] negIfs = { + IfExprent.IF_NE, IfExprent.IF_EQ, IfExprent.IF_GE, IfExprent.IF_LT, IfExprent.IF_LE, IfExprent.IF_GT, IfExprent.IF_NONNULL, + IfExprent.IF_NULL, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPLE, + IfExprent.IF_ICMPGT, IfExprent.IF_ACMPNE, IfExprent.IF_ACMPEQ + }; - private static final int[] func4 = - new int[]{FunctionExprent.FUNCTION_LCMP, FunctionExprent.FUNCTION_FCMPL, FunctionExprent.FUNCTION_FCMPG, FunctionExprent.FUNCTION_DCMPL, - FunctionExprent.FUNCTION_DCMPG}; + private static final String[] typeNames = {"byte", "char", "double", "float", "int", "long", "short", "boolean"}; - private static final int[] func5 = - new int[]{IfExprent.IF_EQ, IfExprent.IF_NE, IfExprent.IF_LT, IfExprent.IF_GE, IfExprent.IF_GT, IfExprent.IF_LE}; + private final MethodDescriptor methodDescriptor; + private final VarProcessor varProcessor; - private static final int[] func6 = - new int[]{IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPGT, IfExprent.IF_ICMPLE, - IfExprent.IF_ACMPEQ, IfExprent.IF_ACMPNE}; - - private static final int[] func7 = new int[]{IfExprent.IF_NULL, IfExprent.IF_NONNULL}; - - private static final int[] func8 = new int[]{MonitorExprent.MONITOR_ENTER, MonitorExprent.MONITOR_EXIT}; - - private static final int[] arr_type = - new int[]{CodeConstants.TYPE_BOOLEAN, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_DOUBLE, - CodeConstants.TYPE_BYTE, CodeConstants.TYPE_SHORT, CodeConstants.TYPE_INT, CodeConstants.TYPE_LONG}; - - private static final int[] negifs = - new int[]{IfExprent.IF_NE, IfExprent.IF_EQ, IfExprent.IF_GE, IfExprent.IF_LT, IfExprent.IF_LE, IfExprent.IF_GT, IfExprent.IF_NONNULL, - IfExprent.IF_NULL, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPLE, - IfExprent.IF_ICMPGT, IfExprent.IF_ACMPNE, - IfExprent.IF_ACMPEQ}; - - private static final String[] typeNames = new String[]{"byte", "char", "double", "float", "int", "long", "short", "boolean",}; - - private VarProcessor varProcessor = (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR); + public ExprProcessor(MethodDescriptor md, VarProcessor varProc) { + methodDescriptor = md; + varProcessor = varProc; + } public void processStatement(RootStatement root, StructClass cl) { - FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); DirectGraph dgraph = flatthelper.buildDirectGraph(root); - // try { - // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot")); - // } catch (Exception ex) { - // ex.printStackTrace(); - // } - // collect finally entry points - Set setFinallyShortRangeEntryPoints = new HashSet(); + Set setFinallyShortRangeEntryPoints = new HashSet<>(); for (List lst : dgraph.mapShortRangeFinallyPaths.values()) { for (FinallyPathWrapper finwrap : lst) { setFinallyShortRangeEntryPoints.add(finwrap.entry); } } - Set setFinallyLongRangeEntryPaths = new HashSet(); + Set setFinallyLongRangeEntryPaths = new HashSet<>(); for (List lst : dgraph.mapLongRangeFinallyPaths.values()) { for (FinallyPathWrapper finwrap : lst) { setFinallyLongRangeEntryPaths.add(finwrap.source + "##" + finwrap.entry); } } - Map mapCatch = new HashMap(); + Map mapCatch = new HashMap<>(); collectCatchVars(root, flatthelper, mapCatch); - Map> mapData = new HashMap>(); + Map> mapData = new HashMap<>(); - LinkedList stack = new LinkedList(); - LinkedList> stackEntryPoint = new LinkedList>(); + LinkedList stack = new LinkedList<>(); + LinkedList> stackEntryPoint = new LinkedList<>(); stack.add(dgraph.first); - stackEntryPoint.add(new LinkedList()); + stackEntryPoint.add(new LinkedList<>()); - Map map = new HashMap(); + Map map = new HashMap<>(); map.put(null, new PrimitiveExprsList()); mapData.put(dgraph.first, map); @@ -222,13 +171,8 @@ public class ExprProcessor implements CodeConstants { } if (isSuccessor) { - - Map mapSucc = mapData.get(nd); - if (mapSucc == null) { - mapData.put(nd, mapSucc = new HashMap()); - } - - LinkedList ndentrypoints = new LinkedList(entrypoints); + Map mapSucc = mapData.computeIfAbsent(nd, k -> new HashMap<>()); + LinkedList ndentrypoints = new LinkedList<>(entrypoints); if (setFinallyLongRangeEntryPaths.contains(node.id + "##" + nd.id)) { ndentrypoints.addLast(node.id); @@ -279,10 +223,14 @@ public class ExprProcessor implements CodeConstants { private static PrimitiveExprsList copyVarExprents(PrimitiveExprsList data) { ExprentStack stack = data.getStack(); + copyEntries(stack); + return data; + } + + public static void copyEntries(List stack) { for (int i = 0; i < stack.size(); i++) { stack.set(i, stack.get(i).copy()); } - return data; } private static void collectCatchVars(Statement stat, FlattenStatementsHelper flatthelper, Map map) { @@ -321,8 +269,7 @@ public class ExprProcessor implements CodeConstants { public void processBlock(BasicBlockStatement stat, PrimitiveExprsList data, StructClass cl) { ConstantPool pool = cl.getPool(); - StructBootstrapMethodsAttribute bootstrap = - (StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); + StructBootstrapMethodsAttribute bootstrap = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); BasicBlock block = stat.getBlock(); @@ -335,40 +282,47 @@ public class ExprProcessor implements CodeConstants { Instruction instr = seq.getInstr(i); Integer bytecode_offset = block.getOldOffset(i); + Set bytecode_offsets = bytecode_offset >= 0 ? Collections.singleton(bytecode_offset) : null; switch (instr.opcode) { case opc_aconst_null: - pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_NULL, null)); + pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_NULL, null, bytecode_offsets)); break; case opc_bipush: case opc_sipush: - pushEx(stack, exprlist, new ConstExprent(instr.getOperand(0), true)); + pushEx(stack, exprlist, new ConstExprent(instr.operand(0), true, bytecode_offsets)); break; case opc_lconst_0: case opc_lconst_1: - pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_LONG, new Long(instr.opcode - opc_lconst_0))); + pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_LONG, (long)(instr.opcode - opc_lconst_0), bytecode_offsets)); break; case opc_fconst_0: case opc_fconst_1: case opc_fconst_2: - pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_FLOAT, new Float(instr.opcode - opc_fconst_0))); + pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_FLOAT, (float)(instr.opcode - opc_fconst_0), bytecode_offsets)); break; case opc_dconst_0: case opc_dconst_1: - pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(instr.opcode - opc_dconst_0))); + pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_DOUBLE, (double)(instr.opcode - opc_dconst_0), bytecode_offsets)); break; case opc_ldc: case opc_ldc_w: case opc_ldc2_w: - PrimitiveConstant cn = pool.getPrimitiveConstant(instr.getOperand(0)); - pushEx(stack, exprlist, new ConstExprent(consts[cn.type - CONSTANT_Integer], cn.value)); + PooledConstant cn = pool.getConstant(instr.operand(0)); + if (cn instanceof PrimitiveConstant) { + pushEx(stack, exprlist, new ConstExprent(consts[cn.type - CONSTANT_Integer], ((PrimitiveConstant)cn).value, bytecode_offsets)); + } + else if (cn instanceof LinkConstant) { + //TODO: for now treat Links as Strings + pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_STRING, ((LinkConstant)cn).elementname, bytecode_offsets)); + } break; case opc_iload: case opc_lload: case opc_fload: case opc_dload: case opc_aload: - pushEx(stack, exprlist, new VarExprent(instr.getOperand(0), vartypes[instr.opcode - opc_iload], varProcessor)); + pushEx(stack, exprlist, new VarExprent(instr.operand(0), varTypes[instr.opcode - opc_iload], varProcessor, bytecode_offset)); break; case opc_iaload: case opc_laload: @@ -389,17 +343,17 @@ public class ExprProcessor implements CodeConstants { case opc_daload: vartype = VarType.VARTYPE_DOUBLE; } - pushEx(stack, exprlist, new ArrayExprent(arr, index, arrtypes[instr.opcode - opc_iaload]), vartype); + pushEx(stack, exprlist, new ArrayExprent(arr, index, arrTypes[instr.opcode - opc_iaload], bytecode_offsets), vartype); break; case opc_istore: case opc_lstore: case opc_fstore: case opc_dstore: case opc_astore: - Exprent top = stack.pop(); - int varindex = instr.getOperand(0); - AssignmentExprent assign = - new AssignmentExprent(new VarExprent(varindex, vartypes[instr.opcode - opc_istore], varProcessor), top); + Exprent expr = stack.pop(); + int varindex = instr.operand(0); + AssignmentExprent assign = new AssignmentExprent( + new VarExprent(varindex, varTypes[instr.opcode - opc_istore], varProcessor, nextMeaningfulOffset(block, i)), expr, bytecode_offsets); exprlist.add(assign); break; case opc_iastore: @@ -414,7 +368,8 @@ public class ExprProcessor implements CodeConstants { Exprent index_store = stack.pop(); Exprent arr_store = stack.pop(); AssignmentExprent arrassign = - new AssignmentExprent(new ArrayExprent(arr_store, index_store, arrtypes[instr.opcode - opc_iastore]), value); + new AssignmentExprent(new ArrayExprent(arr_store, index_store, arrTypes[instr.opcode - opc_iastore], bytecode_offsets), value, + bytecode_offsets); exprlist.add(arrassign); break; case opc_iadd: @@ -437,7 +392,7 @@ public class ExprProcessor implements CodeConstants { case opc_lrem: case opc_frem: case opc_drem: - pushEx(stack, exprlist, new FunctionExprent(func1[(instr.opcode - opc_iadd) / 4], stack)); + pushEx(stack, exprlist, new FunctionExprent(func1[(instr.opcode - opc_iadd) / 4], stack, bytecode_offsets)); break; case opc_ishl: case opc_lshl: @@ -451,19 +406,20 @@ public class ExprProcessor implements CodeConstants { case opc_lor: case opc_ixor: case opc_lxor: - pushEx(stack, exprlist, new FunctionExprent(func2[(instr.opcode - opc_ishl) / 2], stack)); + pushEx(stack, exprlist, new FunctionExprent(func2[(instr.opcode - opc_ishl) / 2], stack, bytecode_offsets)); break; case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: - pushEx(stack, exprlist, new FunctionExprent(FunctionExprent.FUNCTION_NEG, stack)); + pushEx(stack, exprlist, new FunctionExprent(FunctionExprent.FUNCTION_NEG, stack, bytecode_offsets)); break; case opc_iinc: - VarExprent vevar = new VarExprent(instr.getOperand(0), VarType.VARTYPE_INT, varProcessor); + VarExprent vevar = new VarExprent(instr.operand(0), VarType.VARTYPE_INT, varProcessor); exprlist.add(new AssignmentExprent(vevar, new FunctionExprent( - instr.getOperand(1) < 0 ? FunctionExprent.FUNCTION_SUB : FunctionExprent.FUNCTION_ADD, Arrays - .asList(new Exprent[]{vevar.copy(), new ConstExprent(VarType.VARTYPE_INT, new Integer(Math.abs(instr.getOperand(1))))})))); + instr.operand(1) < 0 ? FunctionExprent.FUNCTION_SUB : FunctionExprent.FUNCTION_ADD, Arrays + .asList(vevar.copy(), new ConstExprent(VarType.VARTYPE_INT, Math.abs(instr.operand(1)), null)), + bytecode_offsets), bytecode_offsets)); break; case opc_i2l: case opc_i2f: @@ -480,14 +436,14 @@ public class ExprProcessor implements CodeConstants { case opc_i2b: case opc_i2c: case opc_i2s: - pushEx(stack, exprlist, new FunctionExprent(func3[instr.opcode - opc_i2l], stack)); + pushEx(stack, exprlist, new FunctionExprent(func3[instr.opcode - opc_i2l], stack, bytecode_offsets)); break; case opc_lcmp: case opc_fcmpl: case opc_fcmpg: case opc_dcmpl: case opc_dcmpg: - pushEx(stack, exprlist, new FunctionExprent(func4[instr.opcode - opc_lcmp], stack)); + pushEx(stack, exprlist, new FunctionExprent(func4[instr.opcode - opc_lcmp], stack, bytecode_offsets)); break; case opc_ifeq: case opc_ifne: @@ -495,7 +451,7 @@ public class ExprProcessor implements CodeConstants { case opc_ifge: case opc_ifgt: case opc_ifle: - exprlist.add(new IfExprent(negifs[func5[instr.opcode - opc_ifeq]], stack)); + exprlist.add(new IfExprent(negIfs[func5[instr.opcode - opc_ifeq]], stack, bytecode_offsets)); break; case opc_if_icmpeq: case opc_if_icmpne: @@ -505,15 +461,15 @@ public class ExprProcessor implements CodeConstants { case opc_if_icmple: case opc_if_acmpeq: case opc_if_acmpne: - exprlist.add(new IfExprent(negifs[func6[instr.opcode - opc_if_icmpeq]], stack)); + exprlist.add(new IfExprent(negIfs[func6[instr.opcode - opc_if_icmpeq]], stack, bytecode_offsets)); break; case opc_ifnull: case opc_ifnonnull: - exprlist.add(new IfExprent(negifs[func7[instr.opcode - opc_ifnull]], stack)); + exprlist.add(new IfExprent(negIfs[func7[instr.opcode - opc_ifnull]], stack, bytecode_offsets)); break; case opc_tableswitch: case opc_lookupswitch: - exprlist.add(new SwitchExprent(stack.pop())); + exprlist.add(new SwitchExprent(stack.pop(), bytecode_offsets)); break; case opc_ireturn: case opc_lreturn: @@ -524,51 +480,47 @@ public class ExprProcessor implements CodeConstants { case opc_athrow: exprlist.add(new ExitExprent(instr.opcode == opc_athrow ? ExitExprent.EXIT_THROW : ExitExprent.EXIT_RETURN, instr.opcode == opc_return ? null : stack.pop(), - instr.opcode == opc_athrow - ? null - : ((MethodDescriptor)DecompilerContext - .getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR)).ret)); + instr.opcode == opc_athrow ? null : methodDescriptor.ret, + bytecode_offsets)); break; case opc_monitorenter: case opc_monitorexit: - exprlist.add(new MonitorExprent(func8[instr.opcode - opc_monitorenter], stack.pop())); + exprlist.add(new MonitorExprent(func8[instr.opcode - opc_monitorenter], stack.pop(), bytecode_offsets)); break; case opc_checkcast: case opc_instanceof: - stack.push(new ConstExprent(new VarType(pool.getPrimitiveConstant(instr.getOperand(0)).getString(), true), null)); + stack.push(new ConstExprent(new VarType(pool.getPrimitiveConstant(instr.operand(0)).getString(), true), null, null)); case opc_arraylength: - pushEx(stack, exprlist, new FunctionExprent(mapConsts.get(instr.opcode).intValue(), stack)); + pushEx(stack, exprlist, new FunctionExprent(mapConsts.get(instr.opcode), stack, bytecode_offsets)); break; case opc_getstatic: case opc_getfield: pushEx(stack, exprlist, - new FieldExprent(pool.getLinkConstant(instr.getOperand(0)), instr.opcode == opc_getstatic ? null : stack.pop())); + new FieldExprent(pool.getLinkConstant(instr.operand(0)), instr.opcode == opc_getstatic ? null : stack.pop(), + bytecode_offsets)); break; case opc_putstatic: case opc_putfield: Exprent valfield = stack.pop(); Exprent exprfield = - new FieldExprent(pool.getLinkConstant(instr.getOperand(0)), instr.opcode == opc_putstatic ? null : stack.pop()); - exprlist.add(new AssignmentExprent(exprfield, valfield)); + new FieldExprent(pool.getLinkConstant(instr.operand(0)), instr.opcode == opc_putstatic ? null : stack.pop(), + bytecode_offsets); + exprlist.add(new AssignmentExprent(exprfield, valfield, bytecode_offsets)); break; case opc_invokevirtual: case opc_invokespecial: case opc_invokestatic: case opc_invokeinterface: case opc_invokedynamic: - if (instr.opcode != opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) { - - LinkConstant invoke_constant = pool.getLinkConstant(instr.getOperand(0)); - int dynamic_invokation_type = -1; + if (instr.opcode != opc_invokedynamic || instr.bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) { + LinkConstant invoke_constant = pool.getLinkConstant(instr.operand(0)); + List bootstrap_arguments = null; if (instr.opcode == opc_invokedynamic && bootstrap != null) { - List bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.index1); - LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1); - - dynamic_invokation_type = content_method_handle.index1; + bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.index1); } - InvocationExprent exprinv = new InvocationExprent(instr.opcode, invoke_constant, stack, dynamic_invokation_type); + InvocationExprent exprinv = new InvocationExprent(instr.opcode, invoke_constant, bootstrap_arguments, stack, bytecode_offsets); if (exprinv.getDescriptor().ret.type == CodeConstants.TYPE_VOID) { exprlist.add(exprinv); } @@ -580,15 +532,15 @@ public class ExprProcessor implements CodeConstants { case opc_new: case opc_anewarray: case opc_multianewarray: - int arrdims = (instr.opcode == opc_new) ? 0 : (instr.opcode == opc_anewarray) ? 1 : instr.getOperand(1); - VarType arrtype = new VarType(pool.getPrimitiveConstant(instr.getOperand(0)).getString(), true); + int dimensions = (instr.opcode == opc_new) ? 0 : (instr.opcode == opc_anewarray) ? 1 : instr.operand(1); + VarType arrType = new VarType(pool.getPrimitiveConstant(instr.operand(0)).getString(), true); if (instr.opcode != opc_multianewarray) { - arrtype.arraydim += arrdims; + arrType = arrType.resizeArrayDim(arrType.arrayDim + dimensions); } - pushEx(stack, exprlist, new NewExprent(arrtype, stack, arrdims)); + pushEx(stack, exprlist, new NewExprent(arrType, stack, dimensions, bytecode_offsets)); break; case opc_newarray: - pushEx(stack, exprlist, new NewExprent(new VarType(arr_type[instr.getOperand(0) - 4], 1), stack, 1)); + pushEx(stack, exprlist, new NewExprent(new VarType(arrTypeIds[instr.operand(0) - 4], 1), stack, 1, bytecode_offsets)); break; case opc_dup: pushEx(stack, exprlist, stack.getByOffset(-1).copy()); @@ -597,7 +549,7 @@ public class ExprProcessor implements CodeConstants { insertByOffsetEx(-2, stack, exprlist, -1); break; case opc_dup_x2: - if (stack.getByOffset(-2).getExprType().stack_size == 2) { + if (stack.getByOffset(-2).getExprType().stackSize == 2) { insertByOffsetEx(-2, stack, exprlist, -1); } else { @@ -605,7 +557,7 @@ public class ExprProcessor implements CodeConstants { } break; case opc_dup2: - if (stack.getByOffset(-1).getExprType().stack_size == 2) { + if (stack.getByOffset(-1).getExprType().stackSize == 2) { pushEx(stack, exprlist, stack.getByOffset(-1).copy()); } else { @@ -614,7 +566,7 @@ public class ExprProcessor implements CodeConstants { } break; case opc_dup2_x1: - if (stack.getByOffset(-1).getExprType().stack_size == 2) { + if (stack.getByOffset(-1).getExprType().stackSize == 2) { insertByOffsetEx(-2, stack, exprlist, -1); } else { @@ -623,8 +575,8 @@ public class ExprProcessor implements CodeConstants { } break; case opc_dup2_x2: - if (stack.getByOffset(-1).getExprType().stack_size == 2) { - if (stack.getByOffset(-2).getExprType().stack_size == 2) { + if (stack.getByOffset(-1).getExprType().stackSize == 2) { + if (stack.getByOffset(-2).getExprType().stackSize == 2) { insertByOffsetEx(-2, stack, exprlist, -1); } else { @@ -632,7 +584,7 @@ public class ExprProcessor implements CodeConstants { } } else { - if (stack.getByOffset(-3).getExprType().stack_size == 2) { + if (stack.getByOffset(-3).getExprType().stackSize == 2) { insertByOffsetEx(-3, stack, exprlist, -2); insertByOffsetEx(-3, stack, exprlist, -1); } @@ -647,12 +599,38 @@ public class ExprProcessor implements CodeConstants { stack.pop(); break; case opc_pop: - case opc_pop2: stack.pop(); + break; + case opc_pop2: + if (stack.getByOffset(-1).getExprType().stackSize == 1) { + // Since value at the top of the stack is a value of category 1 (JVMS9 2.11.1) + // we should remove one more item from the stack. + // See JVMS9 pop2 chapter. + stack.pop(); + } + stack.pop(); + break; } } } + private static int nextMeaningfulOffset(BasicBlock block, int index) { + InstructionSequence seq = block.getSeq(); + while (++index < seq.length()) { + switch (seq.getInstr(index).opcode) { + case opc_nop: + case opc_istore: + case opc_lstore: + case opc_fstore: + case opc_dstore: + case opc_astore: + continue; + } + return block.getOldOffset(index); + } + return -1; + } + private void pushEx(ExprentStack stack, List exprlist, Exprent exprent) { pushEx(stack, exprlist, exprent, null); } @@ -662,7 +640,7 @@ public class ExprProcessor implements CodeConstants { VarExprent var = new VarExprent(varindex, vartype == null ? exprent.getExprType() : vartype, varProcessor); var.setStack(true); - exprlist.add(new AssignmentExprent(var, exprent)); + exprlist.add(new AssignmentExprent(var, exprent, null)); stack.push(var.copy()); } @@ -670,20 +648,20 @@ public class ExprProcessor implements CodeConstants { int base = VarExprent.STACK_BASE + stack.size(); - LinkedList lst = new LinkedList(); + LinkedList lst = new LinkedList<>(); for (int i = -1; i >= offset; i--) { Exprent varex = stack.pop(); VarExprent varnew = new VarExprent(base + i + 1, varex.getExprType(), varProcessor); varnew.setStack(true); - exprlist.add(new AssignmentExprent(varnew, varex)); + exprlist.add(new AssignmentExprent(varnew, varex, null)); lst.add(0, (VarExprent)varnew.copy()); } Exprent exprent = lst.get(lst.size() + copyoffset).copy(); VarExprent var = new VarExprent(base + offset, exprent.getExprType(), varProcessor); var.setStack(true); - exprlist.add(new AssignmentExprent(var, exprent)); + exprlist.add(new AssignmentExprent(var, exprent, null)); lst.add(0, (VarExprent)var.copy()); for (VarExprent expr : lst) { @@ -696,7 +674,6 @@ public class ExprProcessor implements CodeConstants { } public static String getTypeName(VarType type, boolean getShort) { - int tp = type.type; if (tp <= CodeConstants.TYPE_BOOLEAN) { return typeNames[tp]; @@ -731,12 +708,9 @@ public class ExprProcessor implements CodeConstants { } public static String getCastTypeName(VarType type, boolean getShort) { - String s = getTypeName(type, getShort); - int dim = type.arraydim; - while (dim-- > 0) { - s += "[]"; - } - return s; + StringBuilder s = new StringBuilder(getTypeName(type, getShort)); + TextUtil.append(s, "[]", type.arrayDim); + return s.toString(); } public static PrimitiveExprsList getExpressionData(VarExprent var) { @@ -744,61 +718,63 @@ public class ExprProcessor implements CodeConstants { VarExprent vartmp = new VarExprent(VarExprent.STACK_BASE, var.getExprType(), var.getProcessor()); vartmp.setStack(true); - prlst.getLstExprents().add(new AssignmentExprent(vartmp, var.copy())); + prlst.getLstExprents().add(new AssignmentExprent(vartmp, var.copy(), null)); prlst.getStack().push(vartmp.copy()); return prlst; } - public static boolean endsWithSemikolon(Exprent expr) { + public static boolean endsWithSemicolon(Exprent expr) { int type = expr.type; return !(type == Exprent.EXPRENT_SWITCH || type == Exprent.EXPRENT_MONITOR || type == Exprent.EXPRENT_IF || - (type == Exprent.EXPRENT_VAR && ((VarExprent)expr) - .isClassdef())); + (type == Exprent.EXPRENT_VAR && ((VarExprent)expr).isClassDef())); } - public static String jmpWrapper(Statement stat, int indent, boolean semicolon, BytecodeMappingTracer tracer) { - String new_line_separator = DecompilerContext.getNewLineSeparator(); + private static void addDeletedGotoInstructionMapping(Statement stat, BytecodeMappingTracer tracer) { + if (stat instanceof BasicBlockStatement) { + BasicBlock block = ((BasicBlockStatement)stat).getBlock(); + List offsets = block.getInstrOldOffsets(); + if (!offsets.isEmpty() && + offsets.size() > block.getSeq().length()) { // some instructions have been deleted, but we still have offsets + tracer.addMapping(offsets.get(offsets.size() - 1)); // add the last offset + } + } + } - StringBuilder buf = new StringBuilder(); - String content = stat.toJava(indent, tracer); - if (!(stat instanceof IfStatement) && !(stat instanceof DoStatement) && !(stat instanceof SequenceStatement)) { - content = Util.rtrim(content); - } - buf.append(content); - if (!(stat instanceof IfStatement) && !(stat instanceof DoStatement) && !(stat instanceof SequenceStatement) && buf.length() != 0) { - buf.append(new_line_separator); - } + public static TextBuffer jmpWrapper(Statement stat, int indent, boolean semicolon, BytecodeMappingTracer tracer) { + TextBuffer buf = stat.toJava(indent, tracer); List lstSuccs = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL); if (lstSuccs.size() == 1) { StatEdge edge = lstSuccs.get(0); if (edge.getType() != StatEdge.TYPE_REGULAR && edge.explicit && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) { - buf.append(InterpreterUtil.getIndentString(indent)); + buf.appendIndent(indent); switch (edge.getType()) { case StatEdge.TYPE_BREAK: + addDeletedGotoInstructionMapping(stat, tracer); buf.append("break"); break; case StatEdge.TYPE_CONTINUE: + addDeletedGotoInstructionMapping(stat, tracer); buf.append("continue"); } if (edge.labeled) { - buf.append(" label").append(edge.closure.id); + buf.append(" label").append(edge.closure.id.toString()); } - buf.append(";").append(new_line_separator); + buf.append(";").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } } if (buf.length() == 0 && semicolon) { - buf.append(InterpreterUtil.getIndentString(indent)).append(";").append(new_line_separator); + buf.appendIndent(indent).append(";").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } - return buf.toString(); + return buf; } public static String buildJavaClassName(String name) { @@ -815,92 +791,59 @@ public class ExprProcessor implements CodeConstants { return res; } - public static String listToJava(List lst, int indent, BytecodeMappingTracer tracer) { + public static TextBuffer listToJava(List lst, int indent, BytecodeMappingTracer tracer) { if (lst == null || lst.isEmpty()) { - return ""; + return new TextBuffer(); } - String indstr = InterpreterUtil.getIndentString(indent); - String new_line_separator = DecompilerContext.getNewLineSeparator(); + TextBuffer buf = new TextBuffer(); + + for (Exprent expr : lst) { + if (buf.length() > 0 && expr.type == Exprent.EXPRENT_VAR && ((VarExprent)expr).isClassDef()) { + // separates local class definition from previous statements + buf.appendLineSeparator(); + tracer.incrementCurrentSourceLine(); + } + + TextBuffer content = expr.toJava(indent, tracer); - StringBuilder buf = new StringBuilder(); - // Spigot Start - boolean inVar = false; - boolean bloodySpecialCaseForNewAssignments = false; - for (Iterator iter = SortUtil.sortIndexed(lst.iterator()); iter.hasNext();) { - Exprent expr = iter.next(); - // Spigot End - String content = expr.toJava(indent, tracer); if (content.length() > 0) { - - if (expr instanceof VarExprent || expr instanceof AssignmentExprent) { - inVar = true; - boolean wasASpecialLittleFlower = bloodySpecialCaseForNewAssignments; - bloodySpecialCaseForNewAssignments = expr instanceof VarExprent; - if (expr instanceof AssignmentExprent && ((AssignmentExprent) expr).getLeft() instanceof VarExprent) { - AssignmentExprent assignmentExprent = (AssignmentExprent) expr; - VarExprent var = (VarExprent) assignmentExprent.getLeft(); - bloodySpecialCaseForNewAssignments = var.isDefinition(); - } - if (wasASpecialLittleFlower && !bloodySpecialCaseForNewAssignments) { - buf.append(new_line_separator); - } - } else if (inVar) { - inVar = false; - if (!(expr instanceof InvocationExprent - || expr instanceof ExitExprent - || expr instanceof FunctionExprent - ) - || bloodySpecialCaseForNewAssignments) { - bloodySpecialCaseForNewAssignments = false; - buf.append(new_line_separator); - } - } - - if (expr.type != Exprent.EXPRENT_VAR || !((VarExprent)expr).isClassdef()) { - buf.append(indstr); + if (expr.type != Exprent.EXPRENT_VAR || !((VarExprent)expr).isClassDef()) { + buf.appendIndent(indent); } buf.append(content); - if (expr.type == Exprent.EXPRENT_MONITOR && ((MonitorExprent)expr).getMontype() == MonitorExprent.MONITOR_ENTER) { + if (expr.type == Exprent.EXPRENT_MONITOR && ((MonitorExprent)expr).getMonType() == MonitorExprent.MONITOR_ENTER) { buf.append("{}"); // empty synchronized block } - if (endsWithSemikolon(expr)) { + if (endsWithSemicolon(expr)) { buf.append(";"); } - buf.append(new_line_separator); - + buf.appendLineSeparator(); tracer.incrementCurrentSourceLine(); } } - return buf.toString(); + return buf; } - public static ConstExprent getDefaultArrayValue(VarType arrtype) { - - ConstExprent defaultval; - if (arrtype.type == CodeConstants.TYPE_OBJECT || arrtype.arraydim > 0) { - defaultval = new ConstExprent(VarType.VARTYPE_NULL, null); + public static ConstExprent getDefaultArrayValue(VarType arrType) { + ConstExprent defaultVal; + if (arrType.type == CodeConstants.TYPE_OBJECT || arrType.arrayDim > 0) { + defaultVal = new ConstExprent(VarType.VARTYPE_NULL, null, null); } - else if (arrtype.type == CodeConstants.TYPE_FLOAT) { - defaultval = new ConstExprent(VarType.VARTYPE_FLOAT, new Float(0)); + else if (arrType.type == CodeConstants.TYPE_FLOAT) { + defaultVal = new ConstExprent(VarType.VARTYPE_FLOAT, 0f, null); } - else if (arrtype.type == CodeConstants.TYPE_LONG) { - defaultval = new ConstExprent(VarType.VARTYPE_LONG, new Long(0)); + else if (arrType.type == CodeConstants.TYPE_LONG) { + defaultVal = new ConstExprent(VarType.VARTYPE_LONG, 0L, null); } - else if (arrtype.type == CodeConstants.TYPE_DOUBLE) { - defaultval = new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(0)); + else if (arrType.type == CodeConstants.TYPE_DOUBLE) { + defaultVal = new ConstExprent(VarType.VARTYPE_DOUBLE, 0d, null); } else { // integer types - defaultval = new ConstExprent(0, true); + defaultVal = new ConstExprent(0, true, null); } - - return defaultval; - } - - public static boolean getCastedExprent(Exprent exprent, VarType leftType, TextBuffer buffer, int indent, - boolean castNull, BytecodeMappingTracer tracer) { - return getCastedExprent(exprent, leftType, buffer, indent, castNull, false, tracer); + return defaultVal; } public static boolean getCastedExprent(Exprent exprent, @@ -908,44 +851,89 @@ public class ExprProcessor implements CodeConstants { TextBuffer buffer, int indent, boolean castNull, - boolean castAlways, BytecodeMappingTracer tracer) { + BytecodeMappingTracer tracer) { + return getCastedExprent(exprent, leftType, buffer, indent, castNull, false, false, false, tracer); + } + + public static boolean getCastedExprent(Exprent exprent, + VarType leftType, + TextBuffer buffer, + int indent, + boolean castNull, + boolean castAlways, + boolean castNarrowing, + boolean unbox, + BytecodeMappingTracer tracer) { + + if (unbox) { + // "unbox" invocation parameters, e.g. 'byteSet.add((byte)123)' or 'new ShortContainer((short)813)' + if (exprent.type == Exprent.EXPRENT_INVOCATION && ((InvocationExprent)exprent).isBoxingCall()) { + InvocationExprent invocationExprent = (InvocationExprent)exprent; + exprent = invocationExprent.getLstParameters().get(0); + int paramType = invocationExprent.getDescriptor().params[0].type; + if (exprent.type == Exprent.EXPRENT_CONST && ((ConstExprent)exprent).getConstType().type != paramType) { + leftType = new VarType(paramType); + } + } + } - boolean ret = false; VarType rightType = exprent.getExprType(); - String res = exprent.toJava(indent, tracer); - boolean cast = - !leftType.isSuperset(rightType) && (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT); - cast |= castAlways && !leftType.equals(rightType); // Spigot + castAlways || + (!leftType.isSuperset(rightType) && (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT)) || + (castNull && rightType.type == CodeConstants.TYPE_NULL && !UNDEFINED_TYPE_STRING.equals(getTypeName(leftType))) || + (castNarrowing && isIntConstant(exprent) && isNarrowedIntType(leftType)); - if (!cast && castNull && rightType.type == CodeConstants.TYPE_NULL) { - // check for a nameless anonymous class - cast = !UNDEFINED_TYPE_STRING.equals(getTypeName(leftType)); - } - if (!cast) { - cast = isIntConstant(exprent) && VarType.VARTYPE_INT.isStrictSuperset(leftType); - } + boolean quote = cast && exprent.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST); - if (cast) { - if (exprent.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) { - res = "(" + res + ")"; + // cast instead to 'byte' / 'short' when int constant is used as a value for 'Byte' / 'Short' + if (castNarrowing && exprent.type == Exprent.EXPRENT_CONST && !((ConstExprent) exprent).isNull()) { + if (leftType.equals(VarType.VARTYPE_BYTE_OBJ)) { + leftType = VarType.VARTYPE_BYTE; + } + else if (leftType.equals(VarType.VARTYPE_SHORT_OBJ)) { + leftType = VarType.VARTYPE_SHORT; } - - res = "(" + getCastTypeName(leftType) + ") " + res; - ret = true; } - buffer.append(res); + String name = getCastTypeName(leftType); - return ret; + if (exprent instanceof NewExprent) + { + if (((NewExprent) exprent).isVarArgParam()) + { + quote = cast = false; + } + } + if (name.equals("Object")) + { + quote = cast = false; + } + + if ((leftType.type == CodeConstants.TYPE_NULL)) + { + quote = cast = false; + } + + if (cast) buffer.append('(').append(name).append(')'); + + if (quote) buffer.append('('); + + if (exprent.type == Exprent.EXPRENT_CONST) { + ((ConstExprent) exprent).adjustConstType(leftType); + } + + buffer.append(exprent.toJava(indent, tracer)); + + if (quote) buffer.append(')'); + + return cast; } private static boolean isIntConstant(Exprent exprent) { - if (exprent.type == Exprent.EXPRENT_CONST) { - ConstExprent cexpr = (ConstExprent)exprent; - switch (cexpr.getConsttype().type) { + switch (((ConstExprent)exprent).getConstType().type) { case CodeConstants.TYPE_BYTE: case CodeConstants.TYPE_BYTECHAR: case CodeConstants.TYPE_SHORT: @@ -957,4 +945,9 @@ public class ExprProcessor implements CodeConstants { return false; } -} + + private static boolean isNarrowedIntType(VarType type) { + return VarType.VARTYPE_INT.isStrictSuperset(type) || + type.equals(VarType.VARTYPE_BYTE_OBJ) || type.equals(VarType.VARTYPE_SHORT_OBJ); + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java index c4cded2..e0bb1b2 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; @@ -28,17 +14,13 @@ public class ExprentStack extends ListStack { pointer = list.getPointer(); } - public Exprent push(Exprent item) { - super.push(item); - - return item; - } - + @Override public Exprent pop() { return this.remove(--pointer); } + @Override public ExprentStack clone() { return new ExprentStack(this); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java index 6f83baf..9f52895 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java @@ -1,21 +1,10 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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.*; +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.code.Instruction; +import org.jetbrains.java.decompiler.code.InstructionSequence; +import org.jetbrains.java.decompiler.code.SimpleInstructionSequence; import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG; @@ -36,8 +25,10 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; 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.VarVersionPaar; +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; import org.jetbrains.java.decompiler.util.InterpreterUtil; @@ -45,30 +36,24 @@ import java.util.*; import java.util.Map.Entry; public class FinallyProcessor { + private final Map finallyBlockIDs = new HashMap<>(); + private final Map catchallBlockIDs = new HashMap<>(); - private Map finallyBlockIDs = new HashMap(); - private Map catchallBlockIDs = new HashMap(); + private final MethodDescriptor methodDescriptor; + private final VarProcessor varProcessor; - private VarProcessor varprocessor; - - public FinallyProcessor(VarProcessor varprocessor) { - this.varprocessor = varprocessor; + public FinallyProcessor(MethodDescriptor md, VarProcessor varProc) { + methodDescriptor = md; + varProcessor = varProc; } - public boolean iterateGraph(StructMethod mt, RootStatement root, ControlFlowGraph graph) { - // return processStatement(mt, root, graph, root); - return processStatementEx(mt, root, graph); - } + public boolean iterateGraph(StructClass cl, StructMethod mt, RootStatement root, ControlFlowGraph graph) { + int bytecodeVersion = mt.getBytecodeVersion(); - private boolean processStatementEx(StructMethod mt, RootStatement root, ControlFlowGraph graph) { - - int bytecode_version = mt.getClassStruct().getBytecodeVersion(); - - LinkedList stack = new LinkedList(); + LinkedList stack = new LinkedList<>(); stack.add(root); while (!stack.isEmpty()) { - Statement stat = stack.removeLast(); Statement parent = stat.getParent(); @@ -83,30 +68,26 @@ public class FinallyProcessor { // do nothing } else if (finallyBlockIDs.containsKey(handler.id)) { - fin.setFinally(true); Integer var = finallyBlockIDs.get(handler.id); - fin.setMonitor(var == null ? null : new VarExprent(var.intValue(), VarType.VARTYPE_INT, varprocessor)); + fin.setMonitor(var == null ? null : new VarExprent(var, VarType.VARTYPE_INT, varProcessor)); } else { - - Record inf = getFinallyInformation(mt, root, fin); + 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 @@ -124,63 +105,7 @@ public class FinallyProcessor { return false; } - - // private boolean processStatement(StructMethod mt, RootStatement root, ControlFlowGraph graph, Statement stat) { - // - // boolean res = false; - // - // for(int i=stat.getStats().size()-1;i>=0;i--) { - // if(processStatement(mt, root, graph, stat.getStats().get(i))) { - // return true; - // } - // } - // - // - // if(stat.type == Statement.TYPE_CATCHALL && !stat.isCopied()) { - // - // CatchAllStatement fin = (CatchAllStatement)stat; - // BasicBlock head = fin.getBasichead().getBlock(); - // BasicBlock handler = fin.getHandler().getBasichead().getBlock(); - // - // if(catchallBlockIDs.containsKey(handler.id)) { - // ; // do nothing - // }else if(finallyBlockIDs.containsKey(handler.id)) { - // - // fin.setFinally(true); - // - // Integer var = finallyBlockIDs.get(handler.id); - // fin.setMonitor(var==null?null:new VarExprent(var.intValue(), VarType.VARTYPE_INT, varprocessor)); - // - // } else { - // - // Object[] inf = getFinallyInformation(mt, root, fin); - // - // if(inf == null) { // inconsistent finally - // catchallBlockIDs.put(handler.id, null); - // } else { - // - // if(DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) { - // finallyBlockIDs.put(handler.id, null); - // } else { - // - // int varindex = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); - // insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf); - // - // finallyBlockIDs.put(handler.id, varindex); - // } - // - // DeadCodeHelper.removeEmptyBlocks(graph); - // DeadCodeHelper.mergeBasicBlocks(graph); - // } - // - // res = true; - // } - // } - // - // return res; - // } - - private static class Record { + private static final class Record { private final int firstCode; private final Map mapLast; @@ -190,10 +115,8 @@ public class FinallyProcessor { } } - - private static Record getFinallyInformation(StructMethod mt, RootStatement root, CatchAllStatement fstat) { - - Map mapLast = new HashMap(); + private Record getFinallyInformation(StructClass cl, StructMethod mt, RootStatement root, CatchAllStatement fstat) { + Map mapLast = new HashMap<>(); BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead(); BasicBlock firstBasicBlock = firstBlockStatement.getBlock(); @@ -209,26 +132,25 @@ public class FinallyProcessor { firstcode = 2; } - ExprProcessor proc = new ExprProcessor(); - proc.processStatement(root, mt.getClassStruct()); + ExprProcessor proc = new ExprProcessor(methodDescriptor, varProcessor); + proc.processStatement(root, cl); SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); ssa.splitVariables(root, mt); List lstExprents = firstBlockStatement.getExprents(); - VarVersionPaar varpaar = new VarVersionPaar((VarExprent)((AssignmentExprent)lstExprents.get(firstcode == 2 ? 1 : 0)).getLeft()); + VarVersionPair varpaar = new VarVersionPair((VarExprent)((AssignmentExprent)lstExprents.get(firstcode == 2 ? 1 : 0)).getLeft()); FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); DirectGraph dgraph = flatthelper.buildDirectGraph(root); - LinkedList stack = new LinkedList(); + LinkedList stack = new LinkedList<>(); stack.add(dgraph.first); - Set setVisited = new HashSet(); + Set setVisited = new HashSet<>(); while (!stack.isEmpty()) { - DirectNode node = stack.removeFirst(); if (setVisited.contains(node)) { @@ -259,7 +181,7 @@ public class FinallyProcessor { boolean found = false; for (Exprent expr : lst) { - if (expr.type == Exprent.EXPRENT_VAR && new VarVersionPaar((VarExprent)expr).equals(varpaar)) { + if (expr.type == Exprent.EXPRENT_VAR && new VarVersionPair((VarExprent)expr).equals(varpaar)) { found = true; break; } @@ -269,7 +191,7 @@ public class FinallyProcessor { found = false; if (exprent.type == Exprent.EXPRENT_EXIT) { ExitExprent exexpr = (ExitExprent)exprent; - if (exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR) { + if (exexpr.getExitType() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR) { found = true; } } @@ -287,7 +209,7 @@ public class FinallyProcessor { if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { AssignmentExprent assexpr = (AssignmentExprent)exprent; if (assexpr.getRight().type == Exprent.EXPRENT_VAR && - new VarVersionPaar((VarExprent)assexpr.getRight()).equals(varpaar)) { + new VarVersionPair((VarExprent)assexpr.getRight()).equals(varpaar)) { Exprent next = null; if (i == node.exprents.size() - 1) { @@ -305,7 +227,7 @@ public class FinallyProcessor { boolean found = false; if (next != null && next.type == Exprent.EXPRENT_EXIT) { ExitExprent exexpr = (ExitExprent)next; - if (exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR + if (exexpr.getExitType() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR && assexpr.getLeft().equals(exexpr.getValue())) { found = true; } @@ -375,8 +297,7 @@ public class FinallyProcessor { int var, Record information, int bytecode_version) { - - Set setCopy = new HashSet(setTry); + Set setCopy = new HashSet<>(setTry); int finallytype = information.firstCode; Map mapLast = information.mapLast; @@ -394,21 +315,15 @@ public class FinallyProcessor { // disable semaphore at statement exit points for (BasicBlock block : setTry) { - List lstSucc = block.getSuccs(); - for (BasicBlock dest : lstSucc) { + for (BasicBlock dest : lstSucc) { // break out - if (!setCopy.contains(dest) && dest != graph.getLast()) { + if (dest != graph.getLast() && !setCopy.contains(dest)) { // disable semaphore SimpleInstructionSequence seq = new SimpleInstructionSequence(); - - seq.addInstruction(ConstantsUtil - .getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, - new int[]{0}), -1); - seq.addInstruction(ConstantsUtil - .getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, - new int[]{var}), -1); + seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), -1); + seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), -1); // build a separate block BasicBlock newblock = new BasicBlock(++graph.last_id); @@ -435,14 +350,10 @@ public class FinallyProcessor { } } - // enable semaphor at the statement entrance + // enable semaphore at the statement entrance SimpleInstructionSequence seq = new SimpleInstructionSequence(); - seq.addInstruction( - ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}), - -1); - seq.addInstruction( - ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), - -1); + seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}), -1); + seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), -1); BasicBlock newhead = new BasicBlock(++graph.last_id); newhead.setSeq(seq); @@ -451,12 +362,8 @@ public class FinallyProcessor { // initialize semaphor with false seq = new SimpleInstructionSequence(); - seq.addInstruction( - ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), - -1); - seq.addInstruction( - ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), - -1); + seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), -1); + seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), -1); BasicBlock newheadinit = new BasicBlock(++graph.last_id); newheadinit.setSeq(seq); @@ -466,7 +373,7 @@ public class FinallyProcessor { setCopy.add(newhead); setCopy.add(newheadinit); - for (BasicBlock hd : new HashSet(newheadinit.getSuccExceptions())) { + for (BasicBlock hd : new HashSet<>(newheadinit.getSuccExceptions())) { ExceptionRangeCFG range = graph.getExceptionRange(hd, newheadinit); if (setCopy.containsAll(range.getProtectedRange())) { @@ -476,10 +383,8 @@ public class FinallyProcessor { } } - private static void insertBlockBefore(ControlFlowGraph graph, BasicBlock oldblock, BasicBlock newblock) { - - List lstTemp = new ArrayList(); + List lstTemp = new ArrayList<>(); lstTemp.addAll(oldblock.getPreds()); lstTemp.addAll(oldblock.getPredExceptions()); @@ -510,9 +415,8 @@ public class FinallyProcessor { } } - private static HashSet getAllBasicBlocks(Statement stat) { - - List lst = new LinkedList(); + private static Set getAllBasicBlocks(Statement stat) { + List lst = new LinkedList<>(); lst.add(stat); int index = 0; @@ -529,7 +433,7 @@ public class FinallyProcessor { } while (index < lst.size()); - HashSet res = new LinkedHashSet(); // Spigot: Fix determinism + Set res = new HashSet<>(); for (Statement st : lst) { res.add(((BasicBlockStatement)st).getBlock()); @@ -538,11 +442,9 @@ public class FinallyProcessor { return res; } - private boolean verifyFinallyEx(ControlFlowGraph graph, CatchAllStatement fstat, Record information) { - - HashSet tryBlocks = getAllBasicBlocks(fstat.getFirst()); - HashSet catchBlocks = getAllBasicBlocks(fstat.getHandler()); + Set tryBlocks = getAllBasicBlocks(fstat.getFirst()); + Set catchBlocks = getAllBasicBlocks(fstat.getHandler()); int finallytype = information.firstCode; Map mapLast = information.mapLast; @@ -571,7 +473,7 @@ public class FinallyProcessor { } // identify start blocks - HashSet startBlocks = new LinkedHashSet(); // Spigot: Fix determinism + Set startBlocks = new HashSet<>(); for (BasicBlock block : tryBlocks) { startBlocks.addAll(block.getSuccs()); } @@ -580,7 +482,7 @@ public class FinallyProcessor { startBlocks.remove(graph.getLast()); startBlocks.removeAll(tryBlocks); - List lstAreas = new ArrayList(); + List lstAreas = new ArrayList<>(); for (BasicBlock start : startBlocks) { @@ -620,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; @@ -634,33 +536,32 @@ public class FinallyProcessor { private Area compareSubgraphsEx(ControlFlowGraph graph, BasicBlock startSample, - HashSet catchBlocks, + Set catchBlocks, BasicBlock startCatch, int finallytype, Map mapLast, boolean skippedFirst) { - class BlockStackEntry { - public BasicBlock blockCatch; - public BasicBlock blockSample; + public final BasicBlock blockCatch; + public final BasicBlock blockSample; // TODO: correct handling (merging) of multiple paths - public List lstStoreVars; + public final List lstStoreVars; - public BlockStackEntry(BasicBlock blockCatch, BasicBlock blockSample, List lstStoreVars) { + BlockStackEntry(BasicBlock blockCatch, BasicBlock blockSample, List lstStoreVars) { this.blockCatch = blockCatch; this.blockSample = blockSample; - this.lstStoreVars = new ArrayList(lstStoreVars); + this.lstStoreVars = new ArrayList<>(lstStoreVars); } } - List stack = new LinkedList(); + List stack = new LinkedList<>(); - Set setSample = new HashSet(); + Set setSample = new HashSet<>(); - Map mapNext = new HashMap(); + Map mapNext = new HashMap<>(); - stack.add(new BlockStackEntry(startCatch, startSample, new ArrayList())); + stack.add(new BlockStackEntry(startCatch, startSample, new ArrayList<>())); while (!stack.isEmpty()) { @@ -693,7 +594,6 @@ public class FinallyProcessor { } } - // exception successors if (isLastBlock && blockSample.getSeq().isEmpty()) { // do nothing, blockSample will be removed anyway @@ -721,8 +621,8 @@ public class FinallyProcessor { if (instrCatch.opcode == CodeConstants.opc_astore && instrSample.opcode == CodeConstants.opc_astore) { - lst = new ArrayList(lst); - lst.add(new int[]{instrCatch.getOperand(0), instrSample.getOperand(0)}); + lst = new ArrayList<>(lst); + lst.add(new int[]{instrCatch.operand(0), instrSample.operand(0)}); } } @@ -740,7 +640,7 @@ public class FinallyProcessor { } if (isLastBlock) { - Set setSuccs = new HashSet(blockSample.getSuccs()); + Set setSuccs = new HashSet<>(blockSample.getSuccs()); setSuccs.removeAll(setSample); for (BlockStackEntry stackent : stack) { @@ -755,11 +655,10 @@ public class FinallyProcessor { } } - return new Area(startSample, setSample, getUniqueNext(graph, new HashSet(mapNext.values()))); + return new Area(startSample, setSample, getUniqueNext(graph, new HashSet<>(mapNext.values()))); } private static BasicBlock getUniqueNext(ControlFlowGraph graph, Set setNext) { - // precondition: there is at most one true exit path in a finally statement BasicBlock next = null; @@ -800,13 +699,11 @@ public class FinallyProcessor { Instruction instrNext = seqNext.getInstr(i); Instruction instrBlock = seqBlock.getInstr(i); - if (instrNext.opcode != instrBlock.opcode || instrNext.wide != instrBlock.wide - || instrNext.operandsCount() != instrBlock.operandsCount()) { + if (!Instruction.equals(instrNext, instrBlock)) { return null; } - - for (int j = 0; j < instrNext.getOperands().length; j++) { - if (instrNext.getOperand(j) != instrBlock.getOperand(j)) { + for (int j = 0; j < instrNext.operandsCount(); j++) { + if (instrNext.operand(j) != instrBlock.operand(j)) { return null; } } @@ -848,7 +745,6 @@ public class FinallyProcessor { int type, int finallytype, List lstStoreVars) { - InstructionSequence seqPattern = pattern.getSeq(); InstructionSequence seqSample = sample.getSeq(); @@ -863,11 +759,11 @@ public class FinallyProcessor { if ((type & 2) > 0) { // last if (finallytype == 0 || finallytype == 2) { - seqPattern.removeInstruction(seqPattern.length() - 1); + seqPattern.removeLast(); } if (finallytype == 2) { - seqPattern.removeInstruction(seqPattern.length() - 1); + seqPattern.removeLast(); } } } @@ -887,18 +783,19 @@ public class FinallyProcessor { } if (seqPattern.length() < seqSample.length()) { // split in two blocks - SimpleInstructionSequence seq = new SimpleInstructionSequence(); + LinkedList oldOffsets = new LinkedList<>(); for (int i = seqSample.length() - 1; i >= seqPattern.length(); i--) { seq.addInstruction(0, seqSample.getInstr(i), -1); + oldOffsets.addFirst(sample.getOldOffset(i)); seqSample.removeInstruction(i); } BasicBlock newblock = new BasicBlock(++graph.last_id); newblock.setSeq(seq); + newblock.getInstrOldOffsets().addAll(oldOffsets); - List lstTemp = new ArrayList(); - lstTemp.addAll(sample.getSuccs()); + List lstTemp = new ArrayList<>(sample.getSuccs()); // move successors for (BasicBlock suc : lstTemp) { @@ -930,19 +827,15 @@ public class FinallyProcessor { } public boolean equalInstructions(Instruction first, Instruction second, List lstStoreVars) { - if (first.opcode != second.opcode || first.wide != second.wide - || first.operandsCount() != second.operandsCount()) { + if (!Instruction.equals(first, second)) { return false; } - if (first.group != CodeConstants.GROUP_JUMP && first.getOperands() != null) { // FIXME: switch comparison - for (int i = 0; i < first.getOperands().length; i++) { - - int firstOp = first.getOperand(i); - int secondOp = second.getOperand(i); - + if (first.group != CodeConstants.GROUP_JUMP) { // FIXME: switch comparison + for (int i = 0; i < first.operandsCount(); i++) { + int firstOp = first.operand(i); + int secondOp = second.operand(i); if (firstOp != secondOp) { - // a-load/store instructions if (first.opcode == CodeConstants.opc_aload || first.opcode == CodeConstants.opc_astore) { for (int[] arr : lstStoreVars) { @@ -961,7 +854,6 @@ public class FinallyProcessor { } private static void deleteArea(ControlFlowGraph graph, Area area) { - BasicBlock start = area.start; BasicBlock next = area.next; @@ -975,14 +867,14 @@ public class FinallyProcessor { } // collect common exception ranges of predecessors and successors - Set setCommonExceptionHandlers = new HashSet(next.getSuccExceptions()); + Set setCommonExceptionHandlers = new HashSet<>(next.getSuccExceptions()); for (BasicBlock pred : start.getPreds()) { setCommonExceptionHandlers.retainAll(pred.getSuccExceptions()); } boolean is_outside_range = false; - Set setPredecessors = new HashSet(start.getPreds()); + Set setPredecessors = new HashSet<>(start.getPreds()); // replace start with next for (BasicBlock pred : setPredecessors) { @@ -1004,7 +896,7 @@ public class FinallyProcessor { is_outside_range = true; } - Set setRemovedExceptionRanges = new HashSet(); + Set setRemovedExceptionRanges = new HashSet<>(); for (BasicBlock handler : block.getSuccExceptions()) { setRemovedExceptionRanges.add(graph.getExceptionRange(handler, block)); } @@ -1019,7 +911,7 @@ public class FinallyProcessor { // shift extern edges on splitted blocks if (block.getSeq().isEmpty() && block.getSuccs().size() == 1) { BasicBlock succs = block.getSuccs().get(0); - for (BasicBlock pred : new ArrayList(block.getPreds())) { + for (BasicBlock pred : new ArrayList<>(block.getPreds())) { if (!setBlocks.contains(pred)) { pred.replaceSuccessor(block, succs); } @@ -1035,18 +927,15 @@ public class FinallyProcessor { } if (is_outside_range) { - // new empty block BasicBlock emptyblock = new BasicBlock(++graph.last_id); - emptyblock.setSeq(new SimpleInstructionSequence()); + graph.getBlocks().addWithKey(emptyblock, emptyblock.id); // add to ranges if necessary - if (setCommonRemovedExceptionRanges != null) { - for (ExceptionRangeCFG range : setCommonRemovedExceptionRanges) { - emptyblock.addSuccessorException(range.getHandler()); - range.getProtectedRange().add(emptyblock); - } + for (ExceptionRangeCFG range : setCommonRemovedExceptionRanges) { + emptyblock.addSuccessorException(range.getHandler()); + range.getProtectedRange().add(emptyblock); } // insert between predecessors and next @@ -1058,7 +947,6 @@ public class FinallyProcessor { } private static void removeExceptionInstructionsEx(BasicBlock block, int blocktype, int finallytype) { - InstructionSequence seq = block.getSeq(); if (finallytype == 3) { // empty finally handler @@ -1075,11 +963,11 @@ public class FinallyProcessor { if ((blocktype & 2) > 0) { // last if (finallytype == 2 || finallytype == 0) { - seq.removeInstruction(seq.length() - 1); + seq.removeLast(); } if (finallytype == 2) { // astore - seq.removeInstruction(seq.length() - 1); + seq.removeLast(); } } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java index 0f5141a..ff45d29 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -25,11 +11,10 @@ import org.jetbrains.java.decompiler.struct.attr.StructAnnotationAttribute; import org.jetbrains.java.decompiler.struct.attr.StructAnnotationParameterAttribute; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; -import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.util.List; -public class IdeaNotNullHelper { +public final class IdeaNotNullHelper { public static boolean removeHardcodedChecks(Statement root, StructMethod mt) { @@ -68,7 +53,7 @@ public class IdeaNotNullHelper { // TODO: FUNCTION_NE also possible if reversed order (in theory) if (ifbranch != null && if_condition.type == Exprent.EXPRENT_FUNCTION && - ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ && + ((FunctionExprent)if_condition).getFuncType() == FunctionExprent.FUNCTION_EQ && ifbranch.type == Statement.TYPE_BASICBLOCK && ifbranch.getExprents().size() == 1 && ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) { @@ -85,11 +70,10 @@ public class IdeaNotNullHelper { boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC); MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); - VBStyleCollection attributes = mt.getAttributes(); // parameter annotations - StructAnnotationParameterAttribute param_annotations = (StructAnnotationParameterAttribute)attributes - .getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS); + StructAnnotationParameterAttribute param_annotations = + mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS); if (param_annotations != null) { List> param_annotations_lists = param_annotations.getParamAnnotations(); @@ -107,8 +91,9 @@ public class IdeaNotNullHelper { List annotations = param_annotations_lists.get(i - shift); for (AnnotationExprent ann : annotations) { - if (ann.getClassname().equals("org/jetbrains/annotations/NotNull")) { + if (ann.getClassName().equals("org/jetbrains/annotations/NotNull")) { is_notnull_check = true; + break; } } } @@ -116,7 +101,7 @@ public class IdeaNotNullHelper { break; } - index += md.params[i].stack_size; + index += md.params[i].stackSize; } } } @@ -127,7 +112,7 @@ public class IdeaNotNullHelper { return false; } - removeParameterCheck(stat, mt); + removeParameterCheck(stat); return true; } @@ -135,7 +120,7 @@ public class IdeaNotNullHelper { return false; } - private static void removeParameterCheck(Statement stat, StructMethod mt) { + private static void removeParameterCheck(Statement stat) { Statement st = stat.getFirst(); while (st.type == Statement.TYPE_SEQUENCE) { @@ -144,11 +129,7 @@ public class IdeaNotNullHelper { IfStatement ifstat = (IfStatement)st; - if (ifstat.getElsestat() == null) { // if - // TODO: - } - else { // if - else - + if (ifstat.getElsestat() != null) { // if - else StatEdge ifedge = ifstat.getIfEdge(); StatEdge elseedge = ifstat.getElseEdge(); @@ -172,28 +153,22 @@ public class IdeaNotNullHelper { private static boolean findAndRemoveReturnCheck(Statement stat, StructMethod mt) { - VBStyleCollection attributes = mt.getAttributes(); - boolean is_notnull_check = false; // method annotation, refers to the return value - StructAnnotationAttribute attr = - (StructAnnotationAttribute)attributes.getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS); + StructAnnotationAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS); if (attr != null) { List annotations = attr.getAnnotations(); for (AnnotationExprent ann : annotations) { - if (ann.getClassname().equals("org/jetbrains/annotations/NotNull")) { + if (ann.getClassName().equals("org/jetbrains/annotations/NotNull")) { is_notnull_check = true; + break; } } } - if (is_notnull_check) { - return removeReturnCheck(stat, mt); - } - - return false; + return is_notnull_check && removeReturnCheck(stat, mt); } @@ -205,7 +180,7 @@ public class IdeaNotNullHelper { Exprent exprent = stat.getExprents().get(0); if (exprent.type == Exprent.EXPRENT_EXIT) { ExitExprent exit_exprent = (ExitExprent)exprent; - if (exit_exprent.getExittype() == ExitExprent.EXIT_RETURN) { + if (exit_exprent.getExitType() == ExitExprent.EXIT_RETURN) { Exprent exprent_value = exit_exprent.getValue(); //if(exprent_value.type == Exprent.EXPRENT_VAR) { // VarExprent var_value = (VarExprent)exprent_value; @@ -214,7 +189,7 @@ public class IdeaNotNullHelper { Exprent if_condition = ifparent.getHeadexprent().getCondition(); if (ifparent.getElsestat() == stat && if_condition.type == Exprent.EXPRENT_FUNCTION && - ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory) + ((FunctionExprent)if_condition).getFuncType() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory) FunctionExprent func = (FunctionExprent)if_condition; Exprent first_param = func.getLstOperands().get(0); @@ -268,7 +243,7 @@ public class IdeaNotNullHelper { Exprent exprent = stat.getExprents().get(0); if (exprent.type == Exprent.EXPRENT_EXIT) { ExitExprent exit_exprent = (ExitExprent)exprent; - if (exit_exprent.getExittype() == ExitExprent.EXIT_RETURN) { + if (exit_exprent.getExitType() == ExitExprent.EXIT_RETURN) { Exprent exprent_value = exit_exprent.getValue(); SequenceStatement sequence = (SequenceStatement)parent; @@ -282,7 +257,7 @@ public class IdeaNotNullHelper { Exprent if_condition = ifstat.getHeadexprent().getCondition(); if (ifstat.iftype == IfStatement.IFTYPE_IF && if_condition.type == Exprent.EXPRENT_FUNCTION && - ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory) + ((FunctionExprent)if_condition).getFuncType() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory) FunctionExprent func = (FunctionExprent)if_condition; Exprent first_param = func.getLstOperands().get(0); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java index b1a955c..66e857c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -23,43 +9,30 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; - - -public class IfHelper { - +import java.util.*; +public final class IfHelper { public static boolean mergeAllIfs(RootStatement root) { - - boolean res = mergeAllIfsRec(root, new HashSet()); - + boolean res = mergeAllIfsRec(root, new HashSet<>()); if (res) { SequenceHelper.condenseSequences(root); } - return res; } - - private static boolean mergeAllIfsRec(Statement stat, HashSet setReorderedIfs) { - + private static boolean mergeAllIfsRec(Statement stat, Set setReorderedIfs) { boolean res = false; if (stat.getExprents() == null) { - while (true) { - boolean changed = false; for (Statement st : stat.getStats()) { - res |= mergeAllIfsRec(st, setReorderedIfs); // collapse composed if's - if (changed = mergeIfs(st, setReorderedIfs)) { + if (mergeIfs(st, setReorderedIfs)) { + changed = true; break; } } @@ -75,9 +48,7 @@ public class IfHelper { return res; } - - public static boolean mergeIfs(Statement statement, HashSet setReorderedIfs) { - + public static boolean mergeIfs(Statement statement, Set setReorderedIfs) { if (statement.type != Statement.TYPE_IF && statement.type != Statement.TYPE_SEQUENCE) { return false; } @@ -85,10 +56,9 @@ public class IfHelper { boolean res = false; while (true) { - boolean updated = false; - List lst = new ArrayList(); + List lst = new ArrayList<>(); if (statement.type == Statement.TYPE_IF) { lst.add(statement); } @@ -99,7 +69,6 @@ public class IfHelper { boolean stsingle = (lst.size() == 1); for (Statement stat : lst) { - if (stat.type == Statement.TYPE_IF) { IfNode rtnode = buildGraph((IfStatement)stat, stsingle); @@ -107,22 +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; } @@ -133,14 +105,13 @@ public class IfHelper { break; } - res |= updated; + res = true; } return res; } private static boolean collapseIfIf(IfNode rtnode) { - if (rtnode.edgetypes.get(0) == 0) { IfNode ifbranch = rtnode.succs.get(0); if (ifbranch.succs.size() == 2) { @@ -158,7 +129,7 @@ public class IfHelper { ifchild.removeSuccessor(ifchild.getAllSuccessorEdges().get(0)); ifparent.getStats().removeWithKey(ifchild.id); - if (ifbranch.edgetypes.get(0).intValue() == 1) { // target null + if (ifbranch.edgetypes.get(0) == 1) { // target null ifparent.setIfstat(null); @@ -196,11 +167,12 @@ public class IfHelper { // merge if conditions IfExprent statexpr = ifparent.getHeadexprent(); - List lstOperands = new ArrayList(); + List lstOperands = new ArrayList<>(); lstOperands.add(statexpr.getCondition()); lstOperands.add(ifchild.getHeadexprent().getCondition()); - statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands)); + statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands, null)); + statexpr.addBytecodeOffsets(ifchild.getHeadexprent().bytecode); return true; } @@ -212,11 +184,9 @@ public class IfHelper { } private static boolean collapseIfElse(IfNode rtnode) { - - if (rtnode.edgetypes.get(0).intValue() == 0) { + if (rtnode.edgetypes.get(0) == 0) { IfNode ifbranch = rtnode.succs.get(0); if (ifbranch.succs.size() == 2) { - // if-else branch if (ifbranch.succs.get(0).value == rtnode.succs.get(1).value) { @@ -229,8 +199,8 @@ public class IfHelper { ifchild.getFirst().removeSuccessor(ifchild.getIfEdge()); ifparent.getStats().removeWithKey(ifchild.id); - if (ifbranch.edgetypes.get(1).intValue() == 1 && - ifbranch.edgetypes.get(0).intValue() == 1) { // target null + if (ifbranch.edgetypes.get(1) == 1 && + ifbranch.edgetypes.get(0) == 1) { // target null ifparent.setIfstat(null); @@ -249,11 +219,11 @@ public class IfHelper { // merge if conditions IfExprent statexpr = ifparent.getHeadexprent(); - List lstOperands = new ArrayList(); + List lstOperands = new ArrayList<>(); lstOperands.add(statexpr.getCondition()); - lstOperands.add(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, - Arrays.asList(new Exprent[]{ifchild.getHeadexprent().getCondition()}))); - statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands)); + lstOperands.add(new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, ifchild.getHeadexprent().getCondition(), null)); + statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands, null)); + statexpr.addBytecodeOffsets(ifchild.getHeadexprent().bytecode); return true; } @@ -264,10 +234,8 @@ public class IfHelper { return false; } - private static boolean collapseElse(IfNode rtnode) { - - if (rtnode.edgetypes.get(1).intValue() == 0) { + if (rtnode.edgetypes.get(1) == 0) { IfNode elsebranch = rtnode.succs.get(1); if (elsebranch.succs.size() == 2) { @@ -304,18 +272,17 @@ public class IfHelper { // merge if conditions IfExprent statexpr = secondif.getHeadexprent(); - List lstOperands = new ArrayList(); + List lstOperands = new ArrayList<>(); lstOperands.add(firstif.getHeadexprent().getCondition()); if (path == 2) { - lstOperands.set(0, new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, - Arrays.asList(new Exprent[]{lstOperands.get(0)}))); + lstOperands.set(0, new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, lstOperands.get(0), null)); } lstOperands.add(statexpr.getCondition()); statexpr - .setCondition(new FunctionExprent(path == 1 ? FunctionExprent.FUNCTION_COR : FunctionExprent.FUNCTION_CADD, lstOperands)); + .setCondition(new FunctionExprent(path == 1 ? FunctionExprent.FUNCTION_COR : FunctionExprent.FUNCTION_CADD, lstOperands, null)); if (secondif.getFirst().getExprents().isEmpty() && !firstif.getFirst().getExprents().isEmpty()) { @@ -328,9 +295,7 @@ public class IfHelper { } } else if (elsebranch.succs.size() == 1) { - if (elsebranch.succs.get(0).value == rtnode.succs.get(0).value) { - IfStatement firstif = (IfStatement)rtnode.value; Statement second = elsebranch.value; @@ -359,7 +324,7 @@ public class IfHelper { // negate the if condition IfExprent statexpr = firstif.getHeadexprent(); statexpr - .setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{statexpr.getCondition()}))); + .setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, statexpr.getCondition(), null)); return true; } @@ -369,9 +334,7 @@ public class IfHelper { return false; } - private static IfNode buildGraph(IfStatement stat, boolean stsingle) { - if (stat.iftype == IfStatement.IFTYPE_IFELSE) { return null; } @@ -433,16 +396,14 @@ public class IfHelper { return res; } - // FIXME: rewrite the entire method!!! keep in mind finally exits!! private static boolean reorderIf(IfStatement ifstat) { - if (ifstat.iftype == IfStatement.IFTYPE_IFELSE) { return false; } - boolean ifdirect = false, elsedirect = false; - boolean noifstat = false, noelsestat = false; + boolean ifdirect, elsedirect; + boolean noifstat = false, noelsestat; boolean ifdirectpath = false, elsedirectpath = false; Statement parent = ifstat.getParent(); @@ -453,32 +414,20 @@ public class IfHelper { if (ifstat.getIfstat() == null) { noifstat = true; - if (ifstat.getIfEdge().getType() == StatEdge.TYPE_FINALLYEXIT) { - ifdirect = true; - } - else { - ifdirect = MergeHelper.isDirectPath(from, ifstat.getIfEdge().getDestination()); - } + ifdirect = ifstat.getIfEdge().getType() == StatEdge.TYPE_FINALLYEXIT || + MergeHelper.isDirectPath(from, ifstat.getIfEdge().getDestination()); } else { List lstSuccs = ifstat.getIfstat().getAllSuccessorEdges(); - if (!lstSuccs.isEmpty() && lstSuccs.get(0).getType() == StatEdge.TYPE_FINALLYEXIT) { - ifdirect = true; - } - else { - ifdirect = hasDirectEndEdge(ifstat.getIfstat(), from); - } + ifdirect = !lstSuccs.isEmpty() && lstSuccs.get(0).getType() == StatEdge.TYPE_FINALLYEXIT || + hasDirectEndEdge(ifstat.getIfstat(), from); } Statement last = parent.type == Statement.TYPE_SEQUENCE ? parent.getStats().getLast() : ifstat; noelsestat = (last == ifstat); - if (!last.getAllSuccessorEdges().isEmpty() && last.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_FINALLYEXIT) { - elsedirect = true; - } - else { - elsedirect = hasDirectEndEdge(last, from); - } + elsedirect = !last.getAllSuccessorEdges().isEmpty() && last.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_FINALLYEXIT || + hasDirectEndEdge(last, from); if (!noelsestat && existsPath(ifstat, ifstat.getAllSuccessorEdges().get(0).getDestination())) { return false; @@ -496,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; } } } @@ -509,7 +457,7 @@ public class IfHelper { SequenceStatement sequence = (SequenceStatement)parent; // build and cut the new else statement - List lst = new ArrayList(); + List lst = new ArrayList<>(); for (int i = sequence.getStats().size() - 1; i >= 0; i--) { Statement sttemp = sequence.getStats().get(i); if (sttemp == ifstat) { @@ -554,7 +502,7 @@ public class IfHelper { // negate the if condition IfExprent statexpr = ifstat.getHeadexprent(); - statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{statexpr.getCondition()}))); + statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, statexpr.getCondition(), null)); if (noelsestat) { StatEdge ifedge = ifstat.getIfEdge(); @@ -597,7 +545,7 @@ public class IfHelper { SequenceStatement sequence = (SequenceStatement)parent; // build and cut the new else statement - List lst = new ArrayList(); + List lst = new ArrayList<>(); for (int i = sequence.getStats().size() - 1; i >= 0; i--) { Statement sttemp = sequence.getStats().get(i); if (sttemp == ifstat) { @@ -698,9 +646,7 @@ public class IfHelper { return false; } - private static Statement getNextStatement(Statement stat) { - Statement parent = stat.getParent(); switch (parent.type) { case Statement.TYPE_ROOT: @@ -722,7 +668,6 @@ public class IfHelper { } private static boolean existsPath(Statement from, Statement to) { - for (StatEdge edge : to.getAllPredecessorEdges()) { if (from.containsStatementStrict(edge.getSource())) { return true; @@ -733,18 +678,17 @@ public class IfHelper { } private static class IfNode { - public Statement value; + public final Statement value; + public final List succs = new ArrayList<>(); + public final List edgetypes = new ArrayList<>(); - public List succs = new ArrayList(); - public List edgetypes = new ArrayList(); - - public IfNode(Statement value) { + IfNode(Statement value) { this.value = value; } public void addChild(IfNode child, int type) { succs.add(child); - edgetypes.add(new Integer(type)); + edgetypes.add(type); } } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java index adeda02..0c94761 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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.*; @@ -21,7 +7,7 @@ import java.util.ArrayList; import java.util.List; -public class InlineSingleBlockHelper { +public final class InlineSingleBlockHelper { public static boolean inlineSingleBlocks(RootStatement root) { @@ -68,7 +54,7 @@ public class InlineSingleBlockHelper { Statement parent = source.getParent(); source.removeSuccessor(edge); - List lst = new ArrayList(); + List lst = new ArrayList<>(); for (int i = seq.getStats().size() - 1; i >= index; i--) { lst.add(0, seq.getStats().remove(i)); } @@ -132,17 +118,14 @@ public class InlineSingleBlockHelper { StatEdge edge = lst.get(0); if (sameCatchRanges(edge)) { - if (edge.explicit) { - return true; - } - else { + if (!edge.explicit) { for (int i = index; i < seq.getStats().size(); i++) { if (!noExitLabels(seq.getStats().get(i), seq)) { return false; } } - return true; } + return true; } // FIXME: count labels properly } @@ -200,21 +183,13 @@ public class InlineSingleBlockHelper { } public static boolean isBreakEdgeLabeled(Statement source, Statement closure) { - if (closure.type == Statement.TYPE_DO || closure.type == Statement.TYPE_SWITCH) { - Statement parent = source.getParent(); - - if (parent == closure) { - return false; - } - else { - return parent.type == Statement.TYPE_DO || parent.type == Statement.TYPE_SWITCH || - isBreakEdgeLabeled(parent, closure); - } + return parent != closure && + (parent.type == Statement.TYPE_DO || parent.type == Statement.TYPE_SWITCH || isBreakEdgeLabeled(parent, closure)); } else { return true; } } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java index 26cc638..bf4d889 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -22,7 +8,7 @@ import java.util.*; import java.util.Map.Entry; -public class LabelHelper { +public final class LabelHelper { public static void cleanUpEdges(RootStatement root) { @@ -33,7 +19,7 @@ public class LabelHelper { liftClosures(root); - lowContinueLabels(root, new HashSet()); + lowContinueLabels(root, new HashSet<>()); lowClosures(root); } @@ -63,7 +49,7 @@ public class LabelHelper { if (dest.type != Statement.TYPE_DUMMYEXIT) { Statement parent = dest.getParent(); - List lst = new ArrayList(); + List lst = new ArrayList<>(); if (parent.type == Statement.TYPE_SEQUENCE) { lst.addAll(parent.getStats()); } @@ -131,14 +117,14 @@ public class LabelHelper { lowContinueLabels(st, edges); } else { - lowContinueLabels(st, new HashSet()); + lowContinueLabels(st, new HashSet<>()); } } } public static void lowClosures(Statement stat) { - for (StatEdge edge : new ArrayList(stat.getLabelEdges())) { + for (StatEdge edge : new ArrayList<>(stat.getLabelEdges())) { if (edge.getType() == StatEdge.TYPE_BREAK) { // FIXME: ? for (Statement st : stat.getStats()) { @@ -181,7 +167,7 @@ public class LabelHelper { private static HashMap> setExplicitEdges(Statement stat) { - HashMap> mapEdges = new HashMap>(); + HashMap> mapEdges = new HashMap<>(); if (stat.getExprents() != null) { return mapEdges; @@ -279,7 +265,7 @@ public class LabelHelper { Statement stlast = swst.getCaseStatements().get(last); if (stlast.getExprents() != null && stlast.getExprents().isEmpty()) { StatEdge edge = stlast.getAllSuccessorEdges().get(0); - mapEdges.put(edge.getDestination(), new ArrayList(Arrays.asList(new StatEdge[]{edge}))); + mapEdges.put(edge.getDestination(), new ArrayList<>(Collections.singletonList(edge))); } else { mapEdges = setExplicitEdges(stlast); @@ -340,7 +326,7 @@ public class LabelHelper { edge.explicit = false; } - mapEdges.put(newedge.getDestination(), new ArrayList(Arrays.asList(new StatEdge[]{newedge}))); + mapEdges.put(newedge.getDestination(), new ArrayList<>(Collections.singletonList(newedge))); } } } @@ -388,7 +374,7 @@ public class LabelHelper { } if (statedge != null) { - mapEdges.put(statedge.getDestination(), new ArrayList(Arrays.asList(new StatEdge[]{statedge}))); + mapEdges.put(statedge.getDestination(), new ArrayList<>(Collections.singletonList(statedge))); } } @@ -420,24 +406,26 @@ public class LabelHelper { } } - private static HashSet[] processStatementLabel(Statement stat) { + private static class LabelSets { + private final Set breaks = new HashSet<>(); + private final Set continues = new HashSet<>(); + } - HashSet setBreak = new HashSet(); - HashSet setContinue = new HashSet(); + private static LabelSets processStatementLabel(Statement stat) { + LabelSets sets = new LabelSets(); if (stat.getExprents() == null) { for (Statement st : stat.getStats()) { - HashSet[] arr = processStatementLabel(st); - - setBreak.addAll(arr[0]); - setContinue.addAll(arr[1]); + LabelSets nested = processStatementLabel(st); + sets.breaks.addAll(nested.breaks); + sets.continues.addAll(nested.continues); } boolean shieldType = (stat.type == Statement.TYPE_DO || stat.type == Statement.TYPE_SWITCH); if (shieldType) { for (StatEdge edge : stat.getLabelEdges()) { - if (edge.explicit && ((edge.getType() == StatEdge.TYPE_BREAK && setBreak.contains(edge.getSource())) || - (edge.getType() == StatEdge.TYPE_CONTINUE && setContinue.contains(edge.getSource())))) { + if (edge.explicit && ((edge.getType() == StatEdge.TYPE_BREAK && sets.breaks.contains(edge.getSource())) || + (edge.getType() == StatEdge.TYPE_CONTINUE && sets.continues.contains(edge.getSource())))) { edge.labeled = false; } } @@ -445,16 +433,16 @@ public class LabelHelper { switch (stat.type) { case Statement.TYPE_DO: - setContinue.clear(); + sets.continues.clear(); case Statement.TYPE_SWITCH: - setBreak.clear(); + sets.breaks.clear(); } } - setBreak.add(stat); - setContinue.add(stat); + sets.breaks.add(stat); + sets.continues.add(stat); - return new HashSet[] { setBreak, setContinue }; + return sets; } public static void replaceContinueWithBreak(Statement stat) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java index d5c9e8f..b82562e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -25,7 +11,7 @@ import java.util.Arrays; import java.util.Set; -public class LoopExtractHelper { +public final class LoopExtractHelper { public static boolean extractLoops(Statement root) { @@ -72,9 +58,7 @@ public class LoopExtractHelper { return res ? 1 : 0; } - private static boolean extractLoop(DoStatement stat) { - if (stat.getLooptype() != DoStatement.LOOP_DO) { return false; } @@ -85,12 +69,7 @@ public class LoopExtractHelper { } } - if (!extractLastIf(stat)) { - return extractFirstIf(stat); - } - else { - return true; - } + return extractLastIf(stat) || extractFirstIf(stat); } private static boolean extractLastIf(DoStatement stat) { @@ -189,7 +168,7 @@ public class LoopExtractHelper { loop.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, loop, target)); - for (StatEdge edge : new ArrayList(block.getLabelEdges())) { + for (StatEdge edge : new ArrayList<>(block.getLabelEdges())) { if (edge.getType() == StatEdge.TYPE_CONTINUE || edge == ifedge) { loop.addLabeledEdge(edge); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java deleted file mode 100644 index 67e9248..0000000 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.modules.decompiler; - -import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement; - -import java.util.List; - -public class LowBreakHelper { - - public static void lowBreakLabels(Statement root) { - - lowBreakLabelsRec(root); - - liftBreakLabels(root); - } - - private static void lowBreakLabelsRec(Statement stat) { - - while (true) { - - boolean found = false; - - for (StatEdge edge : stat.getLabelEdges()) { - if (edge.getType() == StatEdge.TYPE_BREAK) { - Statement minclosure = getMinClosure(stat, edge.getSource()); - if (minclosure != stat) { - minclosure.addLabeledEdge(edge); - edge.labeled = isBreakEdgeLabeled(edge.getSource(), minclosure); - found = true; - break; - } - } - } - - if (!found) { - break; - } - } - - for (Statement st : stat.getStats()) { - lowBreakLabelsRec(st); - } - } - - public static boolean isBreakEdgeLabeled(Statement source, Statement closure) { - - if (closure.type == Statement.TYPE_DO || closure.type == Statement.TYPE_SWITCH) { - - Statement parent = source.getParent(); - - if (parent == closure) { - return false; - } - else { - return isBreakEdgeLabeled(parent, closure) || - (parent.type == Statement.TYPE_DO || parent.type == Statement.TYPE_SWITCH); - } - } - else { - return true; - } - } - - public static Statement getMinClosure(Statement closure, Statement source) { - - while (true) { - - Statement newclosure = null; - - switch (closure.type) { - case Statement.TYPE_SEQUENCE: - Statement last = closure.getStats().getLast(); - - if (isOkClosure(closure, source, last)) { - newclosure = last; - } - break; - case Statement.TYPE_IF: - IfStatement ifclosure = (IfStatement)closure; - if (isOkClosure(closure, source, ifclosure.getIfstat())) { - newclosure = ifclosure.getIfstat(); - } - else if (isOkClosure(closure, source, ifclosure.getElsestat())) { - newclosure = ifclosure.getElsestat(); - } - break; - case Statement.TYPE_TRYCATCH: - for (Statement st : closure.getStats()) { - if (isOkClosure(closure, source, st)) { - newclosure = st; - break; - } - } - break; - case Statement.TYPE_SYNCRONIZED: - Statement body = ((SynchronizedStatement)closure).getBody(); - - if (isOkClosure(closure, source, body)) { - newclosure = body; - } - } - - if (newclosure == null) { - break; - } - - closure = newclosure; - } - - return closure; - } - - private static boolean isOkClosure(Statement closure, Statement source, Statement stat) { - - boolean ok = false; - - if (stat != null && stat.containsStatementStrict(source)) { - - List lst = stat.getAllSuccessorEdges(); - - ok = lst.isEmpty(); - if (!ok) { - StatEdge edge = lst.get(0); - ok = (edge.closure == closure && edge.getType() == StatEdge.TYPE_BREAK); - } - } - - return ok; - } - - - private static void liftBreakLabels(Statement stat) { - - for (Statement st : stat.getStats()) { - liftBreakLabels(st); - } - - - while (true) { - - boolean found = false; - - for (StatEdge edge : stat.getLabelEdges()) { - if (edge.explicit && edge.labeled && edge.getType() == StatEdge.TYPE_BREAK) { - - Statement newclosure = getMaxBreakLift(stat, edge); - - if (newclosure != null) { - newclosure.addLabeledEdge(edge); - edge.labeled = isBreakEdgeLabeled(edge.getSource(), newclosure); - - found = true; - break; - } - } - } - - if (!found) { - break; - } - } - } - - private static Statement getMaxBreakLift(Statement stat, StatEdge edge) { - - Statement closure = null; - Statement newclosure = stat; - - while ((newclosure = getNextBreakLift(newclosure, edge)) != null) { - closure = newclosure; - } - - return closure; - } - - private static Statement getNextBreakLift(Statement stat, StatEdge edge) { - - Statement closure = stat.getParent(); - - while (closure != null && !closure.containsStatementStrict(edge.getDestination())) { - - boolean labeled = isBreakEdgeLabeled(edge.getSource(), closure); - if (closure.isLabeled() || !labeled) { - return closure; - } - - closure = closure.getParent(); - } - - return null; - } -} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java index 71d3d79..77c3dc3 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -26,17 +12,13 @@ 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)) ; - + while (enhanceLoopsRec(root)) /**/; SequenceHelper.condenseSequences(root); } private static boolean enhanceLoopsRec(Statement stat) { - boolean res = false; for (Statement st : stat.getStats()) { @@ -53,7 +35,6 @@ public class MergeHelper { } private static boolean enhanceLoop(DoStatement stat) { - int oldloop = stat.getLooptype(); switch (oldloop) { @@ -77,8 +58,7 @@ public class MergeHelper { return (stat.getLooptype() != oldloop); } - private static boolean matchDoWhile(DoStatement stat) { - + private static void matchDoWhile(DoStatement stat) { // search for an if condition at the end of the loop Statement last = stat.getFirst(); while (last.type == Statement.TYPE_SEQUENCE) { @@ -100,10 +80,9 @@ public class MergeHelper { set.remove(last); if (!set.isEmpty()) { - return false; + return; } - stat.setLooptype(DoStatement.LOOP_DOWHILE); IfExprent ifexpr = (IfExprent)lastif.getHeadexprent().copy(); @@ -135,12 +114,9 @@ public class MergeHelper { } stat.addSuccessor(edge); } - - return true; } } } - return false; } private static boolean matchWhile(DoStatement stat) { @@ -185,7 +161,7 @@ public class MergeHelper { if (firstif == stat.getFirst()) { BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - bstat.setExprents(new ArrayList()); + bstat.setExprents(new ArrayList<>()); stat.replaceStatement(firstif, bstat); } else { @@ -224,7 +200,7 @@ public class MergeHelper { if (firstif.getIfstat() == null) { BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - bstat.setExprents(new ArrayList()); + bstat.setExprents(new ArrayList<>()); ifedge.setSource(bstat); bstat.addSuccessor(ifedge); @@ -291,15 +267,14 @@ public class MergeHelper { } } - private static boolean matchFor(DoStatement stat) { - - Exprent lastDoExprent = null, initDoExprent = null; - Statement lastData = null, preData = null; + private static void matchFor(DoStatement stat) { + Exprent lastDoExprent, initDoExprent; + Statement lastData, preData = null; // get last exprent lastData = getLastDirectData(stat.getFirst()); if (lastData == null || lastData.getExprents().isEmpty()) { - return false; + return; } List lstExpr = lastData.getExprents(); @@ -312,11 +287,9 @@ public class MergeHelper { } } - boolean haslast = issingle || (lastDoExprent.type == Exprent.EXPRENT_ASSIGNMENT || - lastDoExprent.type == Exprent.EXPRENT_FUNCTION); - + boolean haslast = issingle || lastDoExprent.type == Exprent.EXPRENT_ASSIGNMENT || lastDoExprent.type == Exprent.EXPRENT_FUNCTION; if (!haslast) { - return false; + return; } boolean hasinit = false; @@ -350,13 +323,12 @@ public class MergeHelper { } } - if ((hasinit && haslast) || issingle) { // FIXME: issingle sufficient? - + if (hasinit || issingle) { // FIXME: issingle sufficient? Set set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD); set.remove(lastData); if (!set.isEmpty()) { - return false; + return; } stat.setLooptype(DoStatement.LOOP_FOR); @@ -373,8 +345,6 @@ public class MergeHelper { } removeLastEmptyStatement(stat, lastData); } - - return true; } private static void removeLastEmptyStatement(DoStatement dostat, Statement stat) { @@ -382,7 +352,7 @@ public class MergeHelper { if (stat == dostat.getFirst()) { BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); - bstat.setExprents(new ArrayList()); + bstat.setExprents(new ArrayList<>()); dostat.replaceStatement(stat, bstat); } else { @@ -402,20 +372,18 @@ public class MergeHelper { } private static Statement getLastDirectData(Statement stat) { - if (stat.getExprents() != null) { return stat; } - switch (stat.type) { - case Statement.TYPE_SEQUENCE: - for (int i = stat.getStats().size() - 1; i >= 0; i--) { - Statement tmp = getLastDirectData(stat.getStats().get(i)); - if (tmp == null || !tmp.getExprents().isEmpty()) { - return tmp; - } + if (stat.type == Statement.TYPE_SEQUENCE) { + for (int i = stat.getStats().size() - 1; i >= 0; i--) { + Statement tmp = getLastDirectData(stat.getStats().get(i)); + if (tmp == null || !tmp.getExprents().isEmpty()) { + return tmp; } + } } return null; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java index aacb61f..d73535f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; @@ -25,7 +11,6 @@ import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatements import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.struct.gen.VarType; -import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -39,10 +24,10 @@ public class PPandMMHelper { FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); DirectGraph dgraph = flatthelper.buildDirectGraph(root); - LinkedList stack = new LinkedList(); + LinkedList stack = new LinkedList<>(); stack.add(dgraph.first); - HashSet setVisited = new HashSet(); + HashSet setVisited = new HashSet<>(); boolean res = false; @@ -109,8 +94,8 @@ public class PPandMMHelper { FunctionExprent func = (FunctionExprent)as.getRight(); VarType midlayer = null; - if (func.getFunctype() >= FunctionExprent.FUNCTION_I2L && - func.getFunctype() <= FunctionExprent.FUNCTION_I2S) { + if (func.getFuncType() >= FunctionExprent.FUNCTION_I2L && + func.getFuncType() <= FunctionExprent.FUNCTION_I2S) { midlayer = func.getSimpleCastType(); if (func.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) { func = (FunctionExprent)func.getLstOperands().get(0); @@ -120,13 +105,13 @@ public class PPandMMHelper { } } - if (func.getFunctype() == FunctionExprent.FUNCTION_ADD || - func.getFunctype() == FunctionExprent.FUNCTION_SUB) { + if (func.getFuncType() == FunctionExprent.FUNCTION_ADD || + func.getFuncType() == FunctionExprent.FUNCTION_SUB) { Exprent econd = func.getLstOperands().get(0); Exprent econst = func.getLstOperands().get(1); if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && - func.getFunctype() == FunctionExprent.FUNCTION_ADD) { + func.getFuncType() == FunctionExprent.FUNCTION_ADD) { econd = econst; econst = func.getLstOperands().get(0); } @@ -137,8 +122,8 @@ public class PPandMMHelper { VarType condtype = econd.getExprType(); if (left.equals(econd) && (midlayer == null || midlayer.equals(condtype))) { FunctionExprent ret = new FunctionExprent( - func.getFunctype() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI, - Arrays.asList(new Exprent[]{econd})); + func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI, + econd, func.bytecode); ret.setImplicitType(condtype); exprentReplaced = true; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java b/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java index 6dbc887..d3ce7d7 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; @@ -22,7 +8,7 @@ import java.util.List; public class PrimitiveExprsList { - private List lstExprents = new ArrayList(); + private final List lstExprents = new ArrayList<>(); private ExprentStack stack = new ExprentStack(); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java index 5643f22..beaa6c2 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -22,7 +8,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; 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.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.gen.VarType; import java.util.ArrayList; @@ -30,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, @@ -43,7 +29,7 @@ public class SecondaryFunctionsHelper { FunctionExprent.FUNCTION_CADD }; - private static final HashMap mapNumComparisons = new HashMap(); + private static final HashMap mapNumComparisons = new HashMap<>(); static { mapNumComparisons.put(FunctionExprent.FUNCTION_EQ, @@ -56,9 +42,7 @@ public class SecondaryFunctionsHelper { mapNumComparisons.put(FunctionExprent.FUNCTION_LE, new Integer[]{FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE, null}); } - - public static boolean identifySecondaryFunctions(Statement stat) { - + public static boolean identifySecondaryFunctions(Statement stat, VarProcessor varProc) { if (stat.getExprents() == null) { // if(){;}else{...} -> if(!){...} if (stat.type == Statement.TYPE_IF) { @@ -100,24 +84,23 @@ public class SecondaryFunctionsHelper { } } - boolean replaced = true; while (replaced) { replaced = false; - List lstObjects = new ArrayList(stat.getExprents() == null ? stat.getSequentialObjects() : stat.getExprents()); + List lstObjects = new ArrayList<>(stat.getExprents() == null ? stat.getSequentialObjects() : stat.getExprents()); for (int i = 0; i < lstObjects.size(); i++) { Object obj = lstObjects.get(i); if (obj instanceof Statement) { - if (identifySecondaryFunctions((Statement)obj)) { + if (identifySecondaryFunctions((Statement)obj, varProc)) { replaced = true; break; } } else if (obj instanceof Exprent) { - Exprent retexpr = identifySecondaryFunctions((Exprent)obj, true); + Exprent retexpr = identifySecondaryFunctions((Exprent)obj, true, varProc); if (retexpr != null) { if (stat.getExprents() == null) { // only head expressions can be replaced! @@ -136,14 +119,12 @@ public class SecondaryFunctionsHelper { return false; } - - private static Exprent identifySecondaryFunctions(Exprent exprent, boolean statement_level) { - + private static Exprent identifySecondaryFunctions(Exprent exprent, boolean statement_level, VarProcessor varProc) { if (exprent.type == Exprent.EXPRENT_FUNCTION) { FunctionExprent fexpr = (FunctionExprent)exprent; - switch (fexpr.getFunctype()) { - case FunctionExprent.FUNCTION_BOOLNOT: + switch (fexpr.getFuncType()) { + case FunctionExprent.FUNCTION_BOOL_NOT: Exprent retparam = propagateBoolNot(fexpr); @@ -170,26 +151,37 @@ public class SecondaryFunctionsHelper { FunctionExprent funcexpr = (FunctionExprent)expr1; ConstExprent cexpr = (ConstExprent)expr2; - int functype = funcexpr.getFunctype(); + int functype = funcexpr.getFuncType(); if (functype == FunctionExprent.FUNCTION_LCMP || functype == FunctionExprent.FUNCTION_FCMPG || functype == FunctionExprent.FUNCTION_FCMPL || functype == FunctionExprent.FUNCTION_DCMPG || functype == FunctionExprent.FUNCTION_DCMPL) { int desttype = -1; - Integer[] destcons = mapNumComparisons.get(fexpr.getFunctype()); + Integer[] destcons = mapNumComparisons.get(fexpr.getFuncType()); if (destcons != null) { int index = cexpr.getIntValue() + 1; if (index >= 0 && index <= 2) { Integer destcon = destcons[index]; if (destcon != null) { - desttype = destcon.intValue(); + desttype = destcon; } } } if (desttype >= 0) { - return new FunctionExprent(desttype, funcexpr.getLstOperands()); + 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); } } } @@ -202,7 +194,7 @@ public class SecondaryFunctionsHelper { replaced = false; for (Exprent expr : exprent.getAllExprents()) { - Exprent retexpr = identifySecondaryFunctions(expr, false); + Exprent retexpr = identifySecondaryFunctions(expr, false, varProc); if (retexpr != null) { exprent.replaceExprent(expr, retexpr); replaced = true; @@ -216,7 +208,7 @@ public class SecondaryFunctionsHelper { FunctionExprent fexpr = (FunctionExprent)exprent; List lstOperands = fexpr.getLstOperands(); - switch (fexpr.getFunctype()) { + switch (fexpr.getFuncType()) { case FunctionExprent.FUNCTION_XOR: for (int i = 0; i < 2; i++) { Exprent operand = lstOperands.get(i); @@ -227,16 +219,16 @@ public class SecondaryFunctionsHelper { ConstExprent cexpr = (ConstExprent)operand; long val; if (operandtype.type == CodeConstants.TYPE_LONG) { - val = ((Long)cexpr.getValue()).longValue(); + val = (Long)cexpr.getValue(); } else { - val = ((Integer)cexpr.getValue()).intValue(); + val = (Integer)cexpr.getValue(); } if (val == -1) { - List lstBitNotOperand = new ArrayList(); + List lstBitNotOperand = new ArrayList<>(); lstBitNotOperand.add(lstOperands.get(1 - i)); - return new FunctionExprent(FunctionExprent.FUNCTION_BITNOT, lstBitNotOperand); + return new FunctionExprent(FunctionExprent.FUNCTION_BIT_NOT, lstBitNotOperand, fexpr.bytecode); } } } @@ -248,29 +240,29 @@ public class SecondaryFunctionsHelper { for (int i = 0; i < 2; i++) { if (lstOperands.get(i).type == Exprent.EXPRENT_CONST) { ConstExprent cexpr = (ConstExprent)lstOperands.get(i); - int val = ((Integer)cexpr.getValue()).intValue(); + int val = (Integer)cexpr.getValue(); - if ((fexpr.getFunctype() == FunctionExprent.FUNCTION_EQ && val == 1) || - (fexpr.getFunctype() == FunctionExprent.FUNCTION_NE && val == 0)) { + if ((fexpr.getFuncType() == FunctionExprent.FUNCTION_EQ && val == 1) || + (fexpr.getFuncType() == FunctionExprent.FUNCTION_NE && val == 0)) { return lstOperands.get(1 - i); } else { - List lstNotOperand = new ArrayList(); + List lstNotOperand = new ArrayList<>(); lstNotOperand.add(lstOperands.get(1 - i)); - return new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, lstNotOperand); + return new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, lstNotOperand, fexpr.bytecode); } } } } break; - case FunctionExprent.FUNCTION_BOOLNOT: + case FunctionExprent.FUNCTION_BOOL_NOT: if (lstOperands.get(0).type == Exprent.EXPRENT_CONST) { int val = ((ConstExprent)lstOperands.get(0)).getIntValue(); if (val == 0) { - return new ConstExprent(VarType.VARTYPE_BOOLEAN, new Integer(1)); + return new ConstExprent(VarType.VARTYPE_BOOLEAN, 1, fexpr.bytecode); } else { - return new ConstExprent(VarType.VARTYPE_BOOLEAN, new Integer(0)); + return new ConstExprent(VarType.VARTYPE_BOOLEAN, 0, fexpr.bytecode); } } break; @@ -286,7 +278,7 @@ public class SecondaryFunctionsHelper { cexpr2.getExprType().type == CodeConstants.TYPE_BOOLEAN) { if (cexpr1.getIntValue() == 0 && cexpr2.getIntValue() != 0) { - return new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{lstOperands.get(0)})); + return new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, lstOperands.get(0), fexpr.bytecode); } else if (cexpr1.getIntValue() != 0 && cexpr2.getIntValue() == 0) { return lstOperands.get(0); @@ -301,25 +293,23 @@ public class SecondaryFunctionsHelper { case FunctionExprent.FUNCTION_DCMPG: int var = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); VarType type = lstOperands.get(0).getExprType(); - VarProcessor processor = (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR); - FunctionExprent iff = new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(new Exprent[]{ - new FunctionExprent(FunctionExprent.FUNCTION_LT, Arrays.asList(new Exprent[]{new VarExprent(var, type, processor), - ConstExprent.getZeroConstant(type.type)})), - new ConstExprent(VarType.VARTYPE_INT, new Integer(-1)), - new ConstExprent(VarType.VARTYPE_INT, new Integer(1))})); + FunctionExprent iff = new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList( + new FunctionExprent(FunctionExprent.FUNCTION_LT, Arrays.asList(new VarExprent(var, type, varProc), + ConstExprent.getZeroConstant(type.type)), null), + new ConstExprent(VarType.VARTYPE_INT, -1, null), + new ConstExprent(VarType.VARTYPE_INT, 1, null)), null); - FunctionExprent head = new FunctionExprent(FunctionExprent.FUNCTION_EQ, Arrays.asList(new Exprent[]{ - new AssignmentExprent(new VarExprent(var, type, processor), new FunctionExprent(FunctionExprent.FUNCTION_SUB, - Arrays.asList( - new Exprent[]{lstOperands.get(0), - lstOperands.get(1)}))), - ConstExprent.getZeroConstant(type.type)})); + FunctionExprent head = new FunctionExprent(FunctionExprent.FUNCTION_EQ, Arrays.asList( + new AssignmentExprent(new VarExprent(var, type, varProc), + new FunctionExprent(FunctionExprent.FUNCTION_SUB, Arrays.asList(lstOperands.get(0), lstOperands.get(1)), null), + null), + ConstExprent.getZeroConstant(type.type)), null); - processor.setVarType(new VarVersionPaar(var, 0), type); + varProc.setVarType(new VarVersionPair(var, 0), type); - return new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(new Exprent[]{ - head, new ConstExprent(VarType.VARTYPE_INT, new Integer(0)), iff})); + return new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList( + head, new ConstExprent(VarType.VARTYPE_INT, 0, null), iff), fexpr.bytecode); } break; case Exprent.EXPRENT_ASSIGNMENT: // check for conditional assignment @@ -331,8 +321,8 @@ public class SecondaryFunctionsHelper { FunctionExprent func = (FunctionExprent)right; VarType midlayer = null; - if (func.getFunctype() >= FunctionExprent.FUNCTION_I2L && - func.getFunctype() <= FunctionExprent.FUNCTION_I2S) { + if (func.getFuncType() >= FunctionExprent.FUNCTION_I2L && + func.getFuncType() <= FunctionExprent.FUNCTION_I2S) { right = func.getLstOperands().get(0); midlayer = func.getSimpleCastType(); if (right.type == Exprent.EXPRENT_FUNCTION) { @@ -347,7 +337,7 @@ public class SecondaryFunctionsHelper { Exprent cond = null; - switch (func.getFunctype()) { + switch (func.getFuncType()) { case FunctionExprent.FUNCTION_ADD: case FunctionExprent.FUNCTION_AND: case FunctionExprent.FUNCTION_OR: @@ -370,7 +360,7 @@ public class SecondaryFunctionsHelper { if (cond != null && (midlayer == null || midlayer.equals(cond.getExprType()))) { asexpr.setRight(cond); - asexpr.setCondtype(func.getFunctype()); + asexpr.setCondType(func.getFuncType()); } } break; @@ -391,16 +381,17 @@ public class SecondaryFunctionsHelper { if (exprent.type == Exprent.EXPRENT_FUNCTION) { FunctionExprent fexpr = (FunctionExprent)exprent; - if (fexpr.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT) { + if (fexpr.getFuncType() == FunctionExprent.FUNCTION_BOOL_NOT) { Exprent param = fexpr.getLstOperands().get(0); if (param.type == Exprent.EXPRENT_FUNCTION) { FunctionExprent fparam = (FunctionExprent)param; - int ftype = fparam.getFunctype(); + int ftype = fparam.getFuncType(); + boolean canSimplify = false; switch (ftype) { - case FunctionExprent.FUNCTION_BOOLNOT: + case FunctionExprent.FUNCTION_BOOL_NOT: Exprent newexpr = fparam.getLstOperands().get(0); Exprent retexpr = propagateBoolNot(newexpr); return retexpr == null ? newexpr : retexpr; @@ -408,20 +399,31 @@ public class SecondaryFunctionsHelper { case FunctionExprent.FUNCTION_COR: List operands = fparam.getLstOperands(); for (int i = 0; i < operands.size(); i++) { - Exprent newparam = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, - Arrays.asList(new Exprent[]{operands.get(i)})); + Exprent newparam = new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, operands.get(i), operands.get(i).bytecode); Exprent retparam = propagateBoolNot(newparam); operands.set(i, retparam == null ? newparam : retparam); } 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 063d201..2e00b7a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java @@ -1,24 +1,9 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; @@ -28,7 +13,7 @@ import java.util.HashSet; import java.util.List; -public class SequenceHelper { +public final class SequenceHelper { public static void condenseSequences(Statement root) { @@ -39,8 +24,7 @@ public class SequenceHelper { if (stat.type == Statement.TYPE_SEQUENCE) { - List lst = new ArrayList(); - lst.addAll(stat.getStats()); + List lst = new ArrayList<>(stat.getStats()); boolean unfolded = false; @@ -84,7 +68,7 @@ public class SequenceHelper { st.removeSuccessor(edge); } - for (StatEdge edge : new HashSet(st.getLabelEdges())) { + for (StatEdge edge : new HashSet<>(st.getLabelEdges())) { if (edge.getSource() != last) { last.addLabeledEdge(edge); } @@ -244,7 +228,7 @@ public class SequenceHelper { while (true) { - Statement next = null; + Statement next; Statement current = null; boolean found = false; @@ -302,7 +286,7 @@ public class SequenceHelper { BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); if (stat.getExprents() == null) { - bstat.setExprents(new ArrayList()); + bstat.setExprents(new ArrayList<>()); } else { bstat.setExprents(DecHelper.copyExprentList(stat.getExprents())); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java index 90ebe2f..72c845f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java @@ -1,64 +1,74 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.main.rels.ClassWrapper; import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx; import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.match.MatchEngine; import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; +import org.jetbrains.java.decompiler.util.InterpreterUtil; import java.util.*; import java.util.Map.Entry; public class SimplifyExprentsHelper { + @SuppressWarnings("SpellCheckingInspection") private static final MatchEngine class14Builder = new MatchEngine( + "statement type:if iftype:if exprsize:-1\n" + + " exprent position:head type:if\n" + + " exprent type:function functype:eq\n" + + " exprent type:field name:$fieldname$\n" + + " exprent type:constant consttype:null\n" + + " statement type:basicblock\n" + + " exprent position:-1 type:assignment ret:$assignfield$\n" + + " exprent type:var index:$var$\n" + + " exprent type:field name:$fieldname$\n" + + " statement type:sequence statsize:2\n" + + " statement type:trycatch\n" + + " statement type:basicblock exprsize:1\n" + + " exprent type:assignment\n" + + " exprent type:var index:$var$\n" + + " exprent type:invocation invclass:java/lang/Class signature:forName(Ljava/lang/String;)Ljava/lang/Class;\n" + + " exprent position:0 type:constant consttype:string constvalue:$classname$\n" + + " statement type:basicblock exprsize:1\n" + + " exprent type:exit exittype:throw\n" + + " statement type:basicblock exprsize:1\n" + + " exprent type:assignment\n" + + " exprent type:field name:$fieldname$ ret:$field$\n" + + " exprent type:var index:$var$"); - private boolean firstInvocation; + private final boolean firstInvocation; public SimplifyExprentsHelper(boolean firstInvocation) { this.firstInvocation = firstInvocation; } - public boolean simplifyStackVarsStatement(Statement stat, HashSet setReorderedIfs, SSAConstructorSparseEx ssa, StructClass cl) { - + public boolean simplifyStackVarsStatement(Statement stat, Set setReorderedIfs, SSAConstructorSparseEx ssa, StructClass cl) { boolean res = false; - if (stat.getExprents() == null) { + List expressions = stat.getExprents(); + if (expressions == null) { + boolean processClass14 = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4); while (true) { - boolean changed = false; for (Statement st : stat.getStats()) { res |= simplifyStackVarsStatement(st, setReorderedIfs, ssa, cl); - // collapse composed if's - if (changed = IfHelper.mergeIfs(st, setReorderedIfs)) { - break; - } + changed = IfHelper.mergeIfs(st, setReorderedIfs) || // collapse composed if's + buildIff(st, ssa) || // collapse iff ?: statement + processClass14 && collapseInlinedClass14(st); // collapse inlined .class property in version 1.4 and before - // collapse iff ?: statement - if (changed = buildIff(st, ssa)) { + if (changed) { break; } } @@ -71,27 +81,23 @@ public class SimplifyExprentsHelper { } } else { - res |= simplifyStackVarsExprents(stat.getExprents(), cl); + res = simplifyStackVarsExprents(expressions, cl); } return res; } private boolean simplifyStackVarsExprents(List list, StructClass cl) { - boolean res = false; int index = 0; - while (index < list.size()) { - Exprent current = list.get(index); Exprent ret = isSimpleConstructorInvocation(current); if (ret != null) { list.set(index, ret); res = true; - continue; } @@ -100,7 +106,6 @@ public class SimplifyExprentsHelper { if (ret != null) { list.set(index, ret); res = true; - continue; } @@ -108,7 +113,6 @@ public class SimplifyExprentsHelper { if (isMonitorExit(current)) { list.remove(index); res = true; - continue; } @@ -116,7 +120,6 @@ public class SimplifyExprentsHelper { if (isTrivialStackAssignment(current)) { list.remove(index); res = true; - continue; } @@ -124,15 +127,12 @@ public class SimplifyExprentsHelper { break; } - Exprent next = list.get(index + 1); - // constructor invocation if (isConstructorInvocationRemote(list, index)) { list.remove(index); res = true; - continue; } @@ -141,19 +141,17 @@ public class SimplifyExprentsHelper { if (isQualifiedNewGetClass(current, next)) { list.remove(index); res = true; - continue; } } // direct initialization of an array - int arrcount = isArrayInitializer(list, index); - if (arrcount > 0) { - for (int i = 0; i < arrcount; i++) { + int arrCount = isArrayInitializer(list, index); + if (arrCount > 0) { + for (int i = 0; i < arrCount; i++) { list.remove(index + 1); } res = true; - continue; } @@ -161,7 +159,6 @@ public class SimplifyExprentsHelper { if (addArrayInitializer(current, next)) { list.remove(index + 1); res = true; - continue; } @@ -170,30 +167,26 @@ public class SimplifyExprentsHelper { if (func != null) { list.set(index, func); res = true; - continue; } // expr++ and expr-- - if (isIPPorIMM(current, next)) { + if (isIPPorIMM(current, next) || isIPPorIMM2(current, next)) { list.remove(index + 1); res = true; - continue; } // assignment on stack - if (isStackAssignement(current, next)) { + if (isStackAssignment(current, next)) { list.remove(index + 1); res = true; - continue; } - if (!firstInvocation && isStackAssignement2(current, next)) { + if (!firstInvocation && isStackAssignment2(current, next)) { list.remove(index + 1); res = true; - continue; } @@ -204,48 +197,42 @@ public class SimplifyExprentsHelper { } private static boolean addArrayInitializer(Exprent first, Exprent second) { - if (first.type == Exprent.EXPRENT_ASSIGNMENT) { AssignmentExprent as = (AssignmentExprent)first; if (as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) { - NewExprent newex = (NewExprent)as.getRight(); + NewExprent newExpr = (NewExprent)as.getRight(); - if (!newex.getLstArrayElements().isEmpty()) { - - VarExprent arrvar = (VarExprent)as.getLeft(); + if (!newExpr.getLstArrayElements().isEmpty()) { + VarExprent arrVar = (VarExprent)as.getLeft(); if (second.type == Exprent.EXPRENT_ASSIGNMENT) { AssignmentExprent aas = (AssignmentExprent)second; if (aas.getLeft().type == Exprent.EXPRENT_ARRAY) { - ArrayExprent arrex = (ArrayExprent)aas.getLeft(); - if (arrex.getArray().type == Exprent.EXPRENT_VAR && arrvar.equals(arrex.getArray()) - && arrex.getIndex().type == Exprent.EXPRENT_CONST) { + ArrayExprent arrExpr = (ArrayExprent)aas.getLeft(); + if (arrExpr.getArray().type == Exprent.EXPRENT_VAR && + arrVar.equals(arrExpr.getArray()) && + arrExpr.getIndex().type == Exprent.EXPRENT_CONST) { + int constValue = ((ConstExprent)arrExpr.getIndex()).getIntValue(); - int constvalue = ((ConstExprent)arrex.getIndex()).getIntValue(); - - if (constvalue < newex.getLstArrayElements().size()) { - Exprent init = newex.getLstArrayElements().get(constvalue); + if (constValue < newExpr.getLstArrayElements().size()) { + Exprent init = newExpr.getLstArrayElements().get(constValue); if (init.type == Exprent.EXPRENT_CONST) { ConstExprent cinit = (ConstExprent)init; + VarType arrType = newExpr.getNewType().decreaseArrayDim(); + ConstExprent defaultVal = ExprProcessor.getDefaultArrayValue(arrType); - VarType arrtype = newex.getNewtype().copy(); - arrtype.decArrayDim(); + if (cinit.equals(defaultVal)) { + Exprent tempExpr = aas.getRight(); - ConstExprent defaultval = ExprProcessor.getDefaultArrayValue(arrtype); + if (!tempExpr.containsExprent(arrVar)) { + newExpr.getLstArrayElements().set(constValue, tempExpr); - if (cinit.equals(defaultval)) { - - Exprent tempexpr = aas.getRight(); - - if (!tempexpr.containsExprent(arrvar)) { - newex.getLstArrayElements().set(constvalue, tempexpr); - - if (tempexpr.type == Exprent.EXPRENT_NEW) { - NewExprent tempnewex = (NewExprent)tempexpr; - int dims = newex.getNewtype().arraydim; - if (dims > 1 && !tempnewex.getLstArrayElements().isEmpty()) { - tempnewex.setDirectArrayInit(true); + if (tempExpr.type == Exprent.EXPRENT_NEW) { + NewExprent tempNewExpr = (NewExprent)tempExpr; + int dims = newExpr.getNewType().arrayDim; + if (dims > 1 && !tempNewExpr.getLstArrayElements().isEmpty()) { + tempNewExpr.setDirectArrayInit(true); } } @@ -264,27 +251,24 @@ public class SimplifyExprentsHelper { return false; } - private static int isArrayInitializer(List list, int index) { - Exprent current = list.get(index); if (current.type == Exprent.EXPRENT_ASSIGNMENT) { AssignmentExprent as = (AssignmentExprent)current; if (as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) { - NewExprent newex = (NewExprent)as.getRight(); + NewExprent newExpr = (NewExprent)as.getRight(); - if (newex.getExprType().arraydim > 0 && newex.getLstDims().size() == 1 && newex.getLstArrayElements().isEmpty() && - newex.getLstDims().get(0).type == Exprent.EXPRENT_CONST) { + if (newExpr.getExprType().arrayDim > 0 && newExpr.getLstDims().size() == 1 && newExpr.getLstArrayElements().isEmpty() && + newExpr.getLstDims().get(0).type == Exprent.EXPRENT_CONST) { - int size = ((Integer)((ConstExprent)newex.getLstDims().get(0)).getValue()).intValue(); + int size = (Integer)((ConstExprent)newExpr.getLstDims().get(0)).getValue(); if (size == 0) { return 0; } - VarExprent arrvar = (VarExprent)as.getLeft(); - - HashMap mapInit = new HashMap(); + VarExprent arrVar = (VarExprent)as.getLeft(); + Map mapInit = new HashMap<>(); int i = 1; while (index + i < list.size() && i <= size) { @@ -294,17 +278,14 @@ public class SimplifyExprentsHelper { if (expr.type == Exprent.EXPRENT_ASSIGNMENT) { AssignmentExprent aas = (AssignmentExprent)expr; if (aas.getLeft().type == Exprent.EXPRENT_ARRAY) { - ArrayExprent arrex = (ArrayExprent)aas.getLeft(); - if (arrex.getArray().type == Exprent.EXPRENT_VAR && arrvar.equals(arrex.getArray()) - && arrex.getIndex().type == Exprent.EXPRENT_CONST) { - - int constvalue = ((ConstExprent)arrex.getIndex()) - .getIntValue(); // TODO: check for a number type. Failure extremely improbable, but nevertheless... - - if (constvalue < size && !mapInit.containsKey(constvalue)) { - - if (!aas.getRight().containsExprent(arrvar)) { - mapInit.put(constvalue, aas.getRight()); + ArrayExprent arrExpr = (ArrayExprent)aas.getLeft(); + if (arrExpr.getArray().type == Exprent.EXPRENT_VAR && arrVar.equals(arrExpr.getArray()) && + arrExpr.getIndex().type == Exprent.EXPRENT_CONST) { + // TODO: check for a number type. Failure extremely improbable, but nevertheless... + int constValue = ((ConstExprent)arrExpr.getIndex()).getIntValue(); + if (constValue < size && !mapInit.containsKey(constValue)) { + if (!aas.getRight().containsExprent(arrVar)) { + mapInit.put(constValue, aas.getRight()); found = true; } } @@ -321,34 +302,29 @@ public class SimplifyExprentsHelper { double fraction = ((double)mapInit.size()) / size; - if ((arrvar.isStack() && fraction > 0) || (size <= 7 && fraction >= 0.3) || - (size > 7 && fraction >= 0.7)) { - - List lstRet = new ArrayList(); - - VarType arrtype = newex.getNewtype().copy(); - arrtype.decArrayDim(); - - ConstExprent defaultval = ExprProcessor.getDefaultArrayValue(arrtype); + if ((arrVar.isStack() && fraction > 0) || (size <= 7 && fraction >= 0.3) || (size > 7 && fraction >= 0.7)) { + List lstRet = new ArrayList<>(); + VarType arrayType = newExpr.getNewType().decreaseArrayDim(); + ConstExprent defaultVal = ExprProcessor.getDefaultArrayValue(arrayType); for (int j = 0; j < size; j++) { - lstRet.add(defaultval.copy()); + lstRet.add(defaultVal.copy()); } - int dims = newex.getNewtype().arraydim; + int dims = newExpr.getNewType().arrayDim; for (Entry ent : mapInit.entrySet()) { - Exprent tempexpr = ent.getValue(); - lstRet.set(ent.getKey(), tempexpr); + Exprent tempExpr = ent.getValue(); + lstRet.set(ent.getKey(), tempExpr); - if (tempexpr.type == Exprent.EXPRENT_NEW) { - NewExprent tempnewex = (NewExprent)tempexpr; - if (dims > 1 && !tempnewex.getLstArrayElements().isEmpty()) { - tempnewex.setDirectArrayInit(true); + if (tempExpr.type == Exprent.EXPRENT_NEW) { + NewExprent tempNewExpr = (NewExprent)tempExpr; + if (dims > 1 && !tempNewExpr.getLstArrayElements().isEmpty()) { + tempNewExpr.setDirectArrayInit(true); } } } - newex.setLstArrayElements(lstRet); + newExpr.setLstArrayElements(lstRet); return mapInit.size(); } @@ -360,26 +336,20 @@ public class SimplifyExprentsHelper { } private static boolean isTrivialStackAssignment(Exprent first) { - if (first.type == Exprent.EXPRENT_ASSIGNMENT) { AssignmentExprent asf = (AssignmentExprent)first; if (asf.getLeft().type == Exprent.EXPRENT_VAR && asf.getRight().type == Exprent.EXPRENT_VAR) { - VarExprent varleft = (VarExprent)asf.getLeft(); - VarExprent varright = (VarExprent)asf.getRight(); - - if (varleft.getIndex() == varright.getIndex() && varleft.isStack() && - varright.isStack()) { - return true; - } + VarExprent left = (VarExprent)asf.getLeft(); + VarExprent right = (VarExprent)asf.getRight(); + return left.getIndex() == right.getIndex() && left.isStack() && right.isStack(); } } return false; } - private static boolean isStackAssignement2(Exprent first, Exprent second) { // e.g. 1.4-style class invocation - + private static boolean isStackAssignment2(Exprent first, Exprent second) { // e.g. 1.4-style class invocation if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) { AssignmentExprent asf = (AssignmentExprent)first; AssignmentExprent ass = (AssignmentExprent)second; @@ -387,7 +357,7 @@ public class SimplifyExprentsHelper { if (asf.getLeft().type == Exprent.EXPRENT_VAR && ass.getRight().type == Exprent.EXPRENT_VAR && asf.getLeft().equals(ass.getRight()) && ((VarExprent)asf.getLeft()).isStack()) { if (ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack()) { - asf.setRight(new AssignmentExprent(ass.getLeft(), asf.getRight())); + asf.setRight(new AssignmentExprent(ass.getLeft(), asf.getRight(), ass.bytecode)); return true; } } @@ -396,8 +366,7 @@ public class SimplifyExprentsHelper { return false; } - private static boolean isStackAssignement(Exprent first, Exprent second) { - + private static boolean isStackAssignment(Exprent first, Exprent second) { if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) { AssignmentExprent asf = (AssignmentExprent)first; AssignmentExprent ass = (AssignmentExprent)second; @@ -426,20 +395,18 @@ public class SimplifyExprentsHelper { } private static Exprent isPPIorMMI(Exprent first) { - if (first.type == Exprent.EXPRENT_ASSIGNMENT) { AssignmentExprent as = (AssignmentExprent)first; if (as.getRight().type == Exprent.EXPRENT_FUNCTION) { FunctionExprent func = (FunctionExprent)as.getRight(); - if (func.getFunctype() == FunctionExprent.FUNCTION_ADD || - func.getFunctype() == FunctionExprent.FUNCTION_SUB) { + if (func.getFuncType() == FunctionExprent.FUNCTION_ADD || func.getFuncType() == FunctionExprent.FUNCTION_SUB) { Exprent econd = func.getLstOperands().get(0); Exprent econst = func.getLstOperands().get(1); if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && - func.getFunctype() == FunctionExprent.FUNCTION_ADD) { + func.getFuncType() == FunctionExprent.FUNCTION_ADD) { econd = econst; econst = func.getLstOperands().get(0); } @@ -448,9 +415,8 @@ public class SimplifyExprentsHelper { Exprent left = as.getLeft(); if (left.type != Exprent.EXPRENT_VAR && left.equals(econd)) { - FunctionExprent ret = new FunctionExprent( - func.getFunctype() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI, - Arrays.asList(new Exprent[]{econd})); + int type = func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI; + FunctionExprent ret = new FunctionExprent(type, econd, func.bytecode); ret.setImplicitType(VarType.VARTYPE_INT); return ret; } @@ -463,19 +429,18 @@ public class SimplifyExprentsHelper { } private static boolean isIPPorIMM(Exprent first, Exprent second) { - if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_FUNCTION) { AssignmentExprent as = (AssignmentExprent)first; FunctionExprent in = (FunctionExprent)second; - if ((in.getFunctype() == FunctionExprent.FUNCTION_MMI || in.getFunctype() == FunctionExprent.FUNCTION_PPI) && + if ((in.getFuncType() == FunctionExprent.FUNCTION_MMI || in.getFuncType() == FunctionExprent.FUNCTION_PPI) && in.getLstOperands().get(0).equals(as.getRight())) { - if (in.getFunctype() == FunctionExprent.FUNCTION_MMI) { - in.setFunctype(FunctionExprent.FUNCTION_IMM); + if (in.getFuncType() == FunctionExprent.FUNCTION_MMI) { + in.setFuncType(FunctionExprent.FUNCTION_IMM); } else { - in.setFunctype(FunctionExprent.FUNCTION_IPP); + in.setFuncType(FunctionExprent.FUNCTION_IPP); } as.setRight(in); @@ -486,36 +451,77 @@ public class SimplifyExprentsHelper { return false; } + private static boolean isIPPorIMM2(Exprent first, Exprent second) { + if (first.type != Exprent.EXPRENT_ASSIGNMENT || second.type != Exprent.EXPRENT_ASSIGNMENT) { + return false; + } + + AssignmentExprent af = (AssignmentExprent)first; + AssignmentExprent as = (AssignmentExprent)second; + + if (as.getRight().type != Exprent.EXPRENT_FUNCTION) { + return false; + } + + FunctionExprent func = (FunctionExprent)as.getRight(); + + if (func.getFuncType() != FunctionExprent.FUNCTION_ADD && func.getFuncType() != FunctionExprent.FUNCTION_SUB) { + return false; + } + + Exprent econd = func.getLstOperands().get(0); + Exprent econst = func.getLstOperands().get(1); + + if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && func.getFuncType() == FunctionExprent.FUNCTION_ADD) { + econd = econst; + econst = func.getLstOperands().get(0); + } + + if (econst.type == Exprent.EXPRENT_CONST && + ((ConstExprent)econst).hasValueOne() && + af.getLeft().equals(econd) && + af.getRight().equals(as.getLeft()) && + (af.getLeft().getExprentUse() & Exprent.MULTIPLE_USES) != 0) { + int type = func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_IPP : FunctionExprent.FUNCTION_IMM; + + FunctionExprent ret = new FunctionExprent(type, af.getRight(), func.bytecode); + ret.setImplicitType(VarType.VARTYPE_INT); + + af.setRight(ret); + return true; + } + + return false; + } + private static boolean isMonitorExit(Exprent first) { if (first.type == Exprent.EXPRENT_MONITOR) { - MonitorExprent monexpr = (MonitorExprent)first; - if (monexpr.getMontype() == MonitorExprent.MONITOR_EXIT && monexpr.getValue().type == Exprent.EXPRENT_VAR - && !((VarExprent)monexpr.getValue()).isStack()) { - return true; - } + MonitorExprent expr = (MonitorExprent)first; + return expr.getMonType() == MonitorExprent.MONITOR_EXIT && + expr.getValue().type == Exprent.EXPRENT_VAR && + !((VarExprent)expr.getValue()).isStack(); } return false; } private static boolean isQualifiedNewGetClass(Exprent first, Exprent second) { - if (first.type == Exprent.EXPRENT_INVOCATION) { - InvocationExprent invexpr = (InvocationExprent)first; + InvocationExprent invocation = (InvocationExprent)first; - if (!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR && invexpr.getName().equals("getClass") && - invexpr.getStringDescriptor().equals("()Ljava/lang/Class;")) { + if (!invocation.isStatic() && invocation.getInstance().type == Exprent.EXPRENT_VAR && invocation.getName().equals("getClass") && + invocation.getStringDescriptor().equals("()Ljava/lang/Class;")) { List lstExprents = second.getAllExprents(); lstExprents.add(second); for (Exprent expr : lstExprents) { if (expr.type == Exprent.EXPRENT_NEW) { - NewExprent nexpr = (NewExprent)expr; - if (nexpr.getConstructor() != null && !nexpr.getConstructor().getLstParameters().isEmpty() && - nexpr.getConstructor().getLstParameters().get(0).equals(invexpr.getInstance())) { + NewExprent newExpr = (NewExprent)expr; + if (newExpr.getConstructor() != null && !newExpr.getConstructor().getLstParameters().isEmpty() && + newExpr.getConstructor().getLstParameters().get(0).equals(invocation.getInstance())) { - String classname = nexpr.getNewtype().value; + String classname = newExpr.getNewType().value; ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(classname); if (node != null && node.type != ClassNode.CLASS_ROOT) { return true; @@ -529,118 +535,8 @@ public class SimplifyExprentsHelper { return false; } - // private static boolean isConstructorInvocationRemote(List list, int index) { - // - // Exprent current = list.get(index); - // - // if(current.type == Exprent.EXPRENT_ASSIGNMENT) { - // AssignmentExprent as = (AssignmentExprent)current; - // - // if(as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) { - // - // NewExprent newexpr = (NewExprent)as.getRight(); - // VarType newtype = newexpr.getNewtype(); - // VarVersionPaar leftPaar = new VarVersionPaar((VarExprent)as.getLeft()); - // - // if(newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0 && - // newexpr.getConstructor() == null) { - // - // Set setChangedVars = new HashSet(); - // - // for(int i = index + 1; i < list.size(); i++) { - // Exprent remote = list.get(i); - // - // if(remote.type == Exprent.EXPRENT_INVOCATION) { - // InvocationExprent in = (InvocationExprent)remote; - // - // if(in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_VAR - // && as.getLeft().equals(in.getInstance())) { - // - // Set setVars = remote.getAllVariables(); - // setVars.remove(leftPaar); - // setVars.retainAll(setChangedVars); - // - // if(setVars.isEmpty()) { - // - // newexpr.setConstructor(in); - // in.setInstance(null); - // - // if(!setChangedVars.isEmpty()) { // some exprents inbetween - // list.add(index+1, as.copy()); - // list.remove(i+1); - // } else { - // list.set(i, as.copy()); - // } - // - // return true; - // } - // } - // } - // - // boolean isTempAssignment = false; - // - // if(remote.type == Exprent.EXPRENT_ASSIGNMENT) { // ugly solution - // AssignmentExprent asremote = (AssignmentExprent)remote; - // if(asremote.getLeft().type == Exprent.EXPRENT_VAR && - // asremote.getRight().type == Exprent.EXPRENT_VAR) { - // setChangedVars.add(new VarVersionPaar((VarExprent)asremote.getLeft())); - // isTempAssignment = true; - // } - // - // // FIXME: needs to be rewritten - // // propagate (var = new X) forward to the invokation and then reduce - // - //// if(asremote.getLeft().type == Exprent.EXPRENT_VAR) { - //// List lstRightExprents = asremote.getRight().getAllExprents(true); - //// lstRightExprents.add(asremote.getRight()); - //// - //// Set setTempChangedVars = new HashSet(); - //// boolean isTemp = true; - //// - //// for(Exprent expr : lstRightExprents) { - //// if(expr.type != Exprent.EXPRENT_VAR && expr.type != Exprent.EXPRENT_FIELD) { - //// isTemp = false; - //// break; - //// } else if(expr.type == Exprent.EXPRENT_VAR) { - //// setTempChangedVars.add(new VarVersionPaar((VarExprent)expr)); - //// } - //// } - //// - //// if(isTemp) { - //// setChangedVars.addAll(setTempChangedVars); - //// isTempAssignment = true; - //// } - //// } - //// } else if(remote.type == Exprent.EXPRENT_FUNCTION) { - //// FunctionExprent fexpr = (FunctionExprent)remote; - //// if(fexpr.getFunctype() == FunctionExprent.FUNCTION_IPP || fexpr.getFunctype() == FunctionExprent.FUNCTION_IMM - //// || fexpr.getFunctype() == FunctionExprent.FUNCTION_PPI || fexpr.getFunctype() == FunctionExprent.FUNCTION_MMI) { - //// if(fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_VAR) { - //// setChangedVars.add(new VarVersionPaar((VarExprent)fexpr.getLstOperands().get(0))); - //// isTempAssignment = true; - //// } - //// } - // } - // - // if(!isTempAssignment) { - // Set setVars = remote.getAllVariables(); - // if(setVars.contains(leftPaar)) { - // return false; - // } else { - // setChangedVars.addAll(setVars); - // } - // } - // } - // } - // } - // } - // - // return false; - // } - - // propagate (var = new X) forward to the invokation + // propagate (var = new X) forward to the invocation private static boolean isConstructorInvocationRemote(List list, int index) { - Exprent current = list.get(index); if (current.type == Exprent.EXPRENT_ASSIGNMENT) { @@ -648,12 +544,11 @@ public class SimplifyExprentsHelper { if (as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) { - NewExprent newexpr = (NewExprent)as.getRight(); - VarType newtype = newexpr.getNewtype(); - VarVersionPaar leftPaar = new VarVersionPaar((VarExprent)as.getLeft()); - - if (newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0 && newexpr.getConstructor() == null) { + NewExprent newExpr = (NewExprent)as.getRight(); + VarType newType = newExpr.getNewType(); + VarVersionPair leftPair = new VarVersionPair((VarExprent)as.getLeft()); + if (newType.type == CodeConstants.TYPE_OBJECT && newType.arrayDim == 0 && newExpr.getConstructor() == null) { for (int i = index + 1; i < list.size(); i++) { Exprent remote = list.get(i); @@ -664,8 +559,7 @@ public class SimplifyExprentsHelper { if (in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_VAR && as.getLeft().equals(in.getInstance())) { - - newexpr.setConstructor(in); + newExpr.setConstructor(in); in.setInstance(null); list.set(i, as.copy()); @@ -675,8 +569,8 @@ public class SimplifyExprentsHelper { } // check for variable in use - Set setVars = remote.getAllVariables(); - if (setVars.contains(leftPaar)) { // variable used somewhere in between -> exit, need a better reduced code + Set setVars = remote.getAllVariables(); + if (setVars.contains(leftPair)) { // variable used somewhere in between -> exit, need a better reduced code return false; } } @@ -688,7 +582,6 @@ public class SimplifyExprentsHelper { } private static Exprent isLambda(Exprent exprent, StructClass cl) { - List lst = exprent.getAllExprents(); for (Exprent expr : lst) { Exprent ret = isLambda(expr, cl); @@ -701,19 +594,17 @@ public class SimplifyExprentsHelper { InvocationExprent in = (InvocationExprent)exprent; if (in.getInvocationTyp() == InvocationExprent.INVOKE_DYNAMIC) { - String lambda_class_name = cl.qualifiedName + in.getInvokeDynamicClassSuffix(); ClassNode lambda_class = DecompilerContext.getClassProcessor().getMapRootClasses().get(lambda_class_name); if (lambda_class != null) { // real lambda class found, replace invocation with an anonymous class - - NewExprent newexp = new NewExprent(new VarType(lambda_class_name, true), null, 0); - newexp.setConstructor(in); - // note: we don't set the instance to null with in.setInstance(null) like it is done for a common constructor invokation + NewExprent newExpr = new NewExprent(new VarType(lambda_class_name, true), null, 0, in.bytecode); + newExpr.setConstructor(in); + // note: we don't set the instance to null with in.setInstance(null) like it is done for a common constructor invocation // lambda can also be a reference to a virtual method (e.g. String x; ...(x::toString);) // in this case instance will hold the corresponding object - return newexp; + return newExpr; } } } @@ -721,9 +612,7 @@ public class SimplifyExprentsHelper { return null; } - private static Exprent isSimpleConstructorInvocation(Exprent exprent) { - List lst = exprent.getAllExprents(); for (Exprent expr : lst) { Exprent ret = isSimpleConstructorInvocation(expr); @@ -735,48 +624,47 @@ public class SimplifyExprentsHelper { if (exprent.type == Exprent.EXPRENT_INVOCATION) { InvocationExprent in = (InvocationExprent)exprent; if (in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_NEW) { - NewExprent newexp = (NewExprent)in.getInstance(); - newexp.setConstructor(in); + NewExprent newExpr = (NewExprent)in.getInstance(); + newExpr.setConstructor(in); in.setInstance(null); - return newexp; + return newExpr; } } return null; } - private static boolean buildIff(Statement stat, SSAConstructorSparseEx ssa) { - if (stat.type == Statement.TYPE_IF && stat.getExprents() == null) { - IfStatement stif = (IfStatement)stat; - if (stif.iftype == IfStatement.IFTYPE_IFELSE) { - Statement ifstat = stif.getIfstat(); - Statement elsestat = stif.getElsestat(); + IfStatement statement = (IfStatement)stat; + Exprent ifHeadExpr = statement.getHeadexprent(); + Set ifHeadExprBytecode = (ifHeadExpr == null ? null : ifHeadExpr.bytecode); - if (ifstat.getExprents() != null && ifstat.getExprents().size() == 1 - && elsestat.getExprents() != null && elsestat.getExprents().size() == 1 - && ifstat.getAllSuccessorEdges().size() == 1 && elsestat.getAllSuccessorEdges().size() == 1 - && ifstat.getAllSuccessorEdges().get(0).getDestination() == elsestat.getAllSuccessorEdges().get(0).getDestination()) { + if (statement.iftype == IfStatement.IFTYPE_IFELSE) { + Statement ifStatement = statement.getIfstat(); + Statement elseStatement = statement.getElsestat(); - Exprent ifexpr = ifstat.getExprents().get(0); - Exprent elseexpr = elsestat.getExprents().get(0); + if (ifStatement.getExprents() != null && ifStatement.getExprents().size() == 1 && + elseStatement.getExprents() != null && elseStatement.getExprents().size() == 1 && + ifStatement.getAllSuccessorEdges().size() == 1 && elseStatement.getAllSuccessorEdges().size() == 1 && + ifStatement.getAllSuccessorEdges().get(0).getDestination() == elseStatement.getAllSuccessorEdges().get(0).getDestination()) { + Exprent ifExpr = ifStatement.getExprents().get(0); + Exprent elseExpr = elseStatement.getExprents().get(0); - if (ifexpr.type == Exprent.EXPRENT_ASSIGNMENT && elseexpr.type == Exprent.EXPRENT_ASSIGNMENT) { - AssignmentExprent ifas = (AssignmentExprent)ifexpr; - AssignmentExprent elseas = (AssignmentExprent)elseexpr; + if (ifExpr.type == Exprent.EXPRENT_ASSIGNMENT && elseExpr.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent ifAssign = (AssignmentExprent)ifExpr; + AssignmentExprent elseAssign = (AssignmentExprent)elseExpr; - if (ifas.getLeft().type == Exprent.EXPRENT_VAR && elseas.getLeft().type == Exprent.EXPRENT_VAR) { - VarExprent ifvar = (VarExprent)ifas.getLeft(); - VarExprent elsevar = (VarExprent)elseas.getLeft(); - - if (ifvar.getIndex() == elsevar.getIndex() && ifvar.isStack()) { // ifvar.getIndex() >= VarExprent.STACK_BASE) { + if (ifAssign.getLeft().type == Exprent.EXPRENT_VAR && elseAssign.getLeft().type == Exprent.EXPRENT_VAR) { + VarExprent ifVar = (VarExprent)ifAssign.getLeft(); + VarExprent elseVar = (VarExprent)elseAssign.getLeft(); + if (ifVar.getIndex() == elseVar.getIndex() && ifVar.isStack()) { // ifVar.getIndex() >= VarExprent.STACK_BASE) { boolean found = false; - for (Entry> ent : ssa.getPhi().entrySet()) { - if (ent.getKey().var == ifvar.getIndex()) { - if (ent.getValue().contains(ifvar.getVersion()) && ent.getValue().contains(elsevar.getVersion())) { + for (Entry> ent : ssa.getPhi().entrySet()) { + if (ent.getKey().var == ifVar.getIndex()) { + if (ent.getValue().contains(ifVar.getVersion()) && ent.getValue().contains(elseVar.getVersion())) { found = true; break; } @@ -784,62 +672,61 @@ public class SimplifyExprentsHelper { } if (found) { - List data = new ArrayList(); - data.addAll(stif.getFirst().getExprents()); + List data = new ArrayList<>(statement.getFirst().getExprents()); - data.add(new AssignmentExprent(ifvar, new FunctionExprent(FunctionExprent.FUNCTION_IIF, - Arrays.asList(new Exprent[]{ - stif.getHeadexprent().getCondition(), - ifas.getRight(), - elseas.getRight()})))); - stif.setExprents(data); + List operands = Arrays.asList(statement.getHeadexprent().getCondition(), ifAssign.getRight(), elseAssign.getRight()); + data.add(new AssignmentExprent(ifVar, new FunctionExprent(FunctionExprent.FUNCTION_IIF, operands, ifHeadExprBytecode), ifHeadExprBytecode)); + statement.setExprents(data); - if (stif.getAllSuccessorEdges().isEmpty()) { - StatEdge ifedge = ifstat.getAllSuccessorEdges().get(0); - StatEdge edge = new StatEdge(ifedge.getType(), stif, ifedge.getDestination()); + if (statement.getAllSuccessorEdges().isEmpty()) { + StatEdge ifEdge = ifStatement.getAllSuccessorEdges().get(0); + StatEdge edge = new StatEdge(ifEdge.getType(), statement, ifEdge.getDestination()); - stif.addSuccessor(edge); - if (ifedge.closure != null) { - ifedge.closure.addLabeledEdge(edge); + statement.addSuccessor(edge); + if (ifEdge.closure != null) { + ifEdge.closure.addLabeledEdge(edge); } } - SequenceHelper.destroyAndFlattenStatement(stif); + SequenceHelper.destroyAndFlattenStatement(statement); return true; } } } } - else if (ifexpr.type == Exprent.EXPRENT_EXIT && elseexpr.type == Exprent.EXPRENT_EXIT) { - ExitExprent ifex = (ExitExprent)ifexpr; - ExitExprent elseex = (ExitExprent)elseexpr; - - if (ifex.getExittype() == elseex.getExittype() && ifex.getValue() != null && elseex.getValue() != null && - ifex.getExittype() == ExitExprent.EXIT_RETURN) { + else if (ifExpr.type == Exprent.EXPRENT_EXIT && elseExpr.type == Exprent.EXPRENT_EXIT) { + ExitExprent ifExit = (ExitExprent)ifExpr; + ExitExprent elseExit = (ExitExprent)elseExpr; + if (ifExit.getExitType() == elseExit.getExitType() && ifExit.getValue() != null && elseExit.getValue() != null && + ifExit.getExitType() == ExitExprent.EXIT_RETURN) { // throw is dangerous, because of implicit casting to a common superclass // e.g. throws IOException and throw true?new RuntimeException():new IOException(); won't work - if (ifex.getExittype() == ExitExprent.EXIT_THROW && - !ifex.getValue().getExprType().equals(elseex.getValue().getExprType())) { // note: getExprType unreliable at this point! + if (ifExit.getExitType() == ExitExprent.EXIT_THROW && + !ifExit.getValue().getExprType().equals(elseExit.getValue().getExprType())) { // note: getExprType unreliable at this point! return false; } - List data = new ArrayList(); - data.addAll(stif.getFirst().getExprents()); + // avoid flattening to 'iff' if any of the branches is an 'iff' already + if (isIff(ifExit.getValue()) || isIff(elseExit.getValue())) { + return false; + } - data.add(new ExitExprent(ifex.getExittype(), new FunctionExprent(FunctionExprent.FUNCTION_IIF, - Arrays.asList(new Exprent[]{ - stif.getHeadexprent().getCondition(), - ifex.getValue(), - elseex.getValue()})), ifex.getRettype())); - stif.setExprents(data); + List data = new ArrayList<>(statement.getFirst().getExprents()); - StatEdge retedge = ifstat.getAllSuccessorEdges().get(0); - stif.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, stif, retedge.getDestination(), - retedge.closure == stif ? stif.getParent() : retedge.closure)); + data.add(new ExitExprent(ifExit.getExitType(), new FunctionExprent(FunctionExprent.FUNCTION_IIF, + Arrays.asList( + statement.getHeadexprent().getCondition(), + ifExit.getValue(), + elseExit.getValue()), ifHeadExprBytecode), ifExit.getRetType(), ifHeadExprBytecode)); + statement.setExprents(data); - SequenceHelper.destroyAndFlattenStatement(stif); + StatEdge retEdge = ifStatement.getAllSuccessorEdges().get(0); + Statement closure = retEdge.closure == statement ? statement.getParent() : retEdge.closure; + statement.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, statement, retEdge.getDestination(), closure)); + + SequenceHelper.destroyAndFlattenStatement(statement); return true; } @@ -850,4 +737,32 @@ public class SimplifyExprentsHelper { return false; } -} + + private static boolean isIff(Exprent exp) { + return exp.type == Exprent.EXPRENT_FUNCTION && ((FunctionExprent) exp).getFuncType() == FunctionExprent.FUNCTION_IIF; + } + + private static boolean collapseInlinedClass14(Statement stat) { + boolean ret = class14Builder.match(stat); + if (ret) { + String class_name = (String)class14Builder.getVariableValue("$classname$"); + AssignmentExprent assignment = (AssignmentExprent)class14Builder.getVariableValue("$assignfield$"); + FieldExprent fieldExpr = (FieldExprent)class14Builder.getVariableValue("$field$"); + + assignment.replaceExprent(assignment.getRight(), new ConstExprent(VarType.VARTYPE_CLASS, class_name, null)); + + List data = new ArrayList<>(stat.getFirst().getExprents()); + + stat.setExprents(data); + + SequenceHelper.destroyAndFlattenStatement(stat); + + ClassWrapper wrapper = (ClassWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_WRAPPER); + if (wrapper != null) { + wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fieldExpr.getName(), fieldExpr.getDescriptor().descriptorString)); + } + } + + return ret; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java index 109490c..1232e64 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -23,7 +9,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.VarVersionEdge; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionNode; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsGraph; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; @@ -34,36 +20,22 @@ import org.jetbrains.java.decompiler.util.SFormsFastMapDirect; import java.util.*; import java.util.Map.Entry; - public class StackVarsProcessor { - public void simplifyStackVars(RootStatement root, StructMethod mt, StructClass cl) { - - HashSet setReorderedIfs = new HashSet(); - + Set setReorderedIfs = new HashSet<>(); SSAUConstructorSparseEx ssau = null; while (true) { - boolean found = false; - // System.out.println("--------------- \r\n"+root.toJava()); - SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); ssa.splitVariables(root, mt); - // System.out.println("--------------- \r\n"+root.toJava()); - - SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(ssau == null); while (sehelper.simplifyStackVarsStatement(root, setReorderedIfs, ssa, cl)) { - // System.out.println("--------------- \r\n"+root.toJava()); found = true; } - - // System.out.println("=============== \r\n"+root.toJava()); - setVersionsToNull(root); SequenceHelper.condenseSequences(root); @@ -71,21 +43,10 @@ public class StackVarsProcessor { ssau = new SSAUConstructorSparseEx(); ssau.splitVariables(root, mt); - // try { - // DotExporter.toDotFile(ssau.getSsuversions(), new File("c:\\Temp\\gr12_my.dot")); - // } catch(Exception ex) { - // ex.printStackTrace(); - // } - - // System.out.println("++++++++++++++++ \r\n"+root.toJava()); - - if (iterateStatements(root, ssau)) { found = true; } - // System.out.println("***************** \r\n"+root.toJava()); - setVersionsToNull(root); if (!found) { @@ -97,21 +58,12 @@ public class StackVarsProcessor { ssau = new SSAUConstructorSparseEx(); ssau.splitVariables(root, mt); - // try { - // DotExporter.toDotFile(ssau.getSsuversions(), new File("c:\\Temp\\gr12_my.dot")); - // } catch(Exception ex) { - // ex.printStackTrace(); - // } - iterateStatements(root, ssau); - // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - setVersionsToNull(root); } private static void setVersionsToNull(Statement stat) { - if (stat.getExprents() == null) { for (Object obj : stat.getSequentialObjects()) { if (obj instanceof Statement) { @@ -130,7 +82,6 @@ public class StackVarsProcessor { } private static void setExprentVersionsToNull(Exprent exprent) { - List lst = exprent.getAllExprents(true); lst.add(exprent); @@ -141,32 +92,29 @@ public class StackVarsProcessor { } } - private boolean iterateStatements(RootStatement root, SSAUConstructorSparseEx ssa) { - FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); DirectGraph dgraph = flatthelper.buildDirectGraph(root); boolean res = false; - HashSet setVisited = new HashSet(); - LinkedList stack = new LinkedList(); - LinkedList> stackMaps = new LinkedList>(); + Set setVisited = new HashSet<>(); + LinkedList stack = new LinkedList<>(); + LinkedList> stackMaps = new LinkedList<>(); stack.add(dgraph.first); - stackMaps.add(new HashMap()); + stackMaps.add(new HashMap<>()); while (!stack.isEmpty()) { - DirectNode nd = stack.removeFirst(); - HashMap mapVarValues = stackMaps.removeFirst(); + Map mapVarValues = stackMaps.removeFirst(); if (setVisited.contains(nd)) { continue; } setVisited.add(nd); - List> lstLists = new ArrayList>(); + List> lstLists = new ArrayList<>(); if (!nd.exprents.isEmpty()) { lstLists.add(nd.exprents); @@ -196,9 +144,6 @@ public class StackVarsProcessor { } int[] ret = iterateExprent(lst, index, next, mapVarValues, ssa); - - //System.out.println("***************** \r\n"+root.toJava()); - if (ret[0] >= 0) { index = ret[0]; } @@ -211,7 +156,7 @@ public class StackVarsProcessor { for (DirectNode ndx : nd.succs) { stack.add(ndx); - stackMaps.add(new HashMap(mapVarValues)); + stackMaps.add(new HashMap<>(mapVarValues)); } // make sure the 3 special exprent lists in a loop (init, condition, increment) are not empty @@ -235,28 +180,23 @@ public class StackVarsProcessor { return res; } - - private static Exprent isReplaceableVar(Exprent exprent, HashMap mapVarValues, SSAUConstructorSparseEx ssau) { - + private static Exprent isReplaceableVar(Exprent exprent, Map mapVarValues) { Exprent dest = null; - if (exprent.type == Exprent.EXPRENT_VAR) { VarExprent var = (VarExprent)exprent; - dest = mapVarValues.get(new VarVersionPaar(var)); + dest = mapVarValues.get(new VarVersionPair(var)); } - return dest; } private static void replaceSingleVar(Exprent parent, VarExprent var, Exprent dest, SSAUConstructorSparseEx ssau) { - parent.replaceExprent(var, dest); // live sets - SFormsFastMapDirect livemap = ssau.getLiveVarVersionsMap(new VarVersionPaar(var)); - HashSet setVars = getAllVersions(dest); + SFormsFastMapDirect livemap = ssau.getLiveVarVersionsMap(new VarVersionPair(var)); + Set setVars = getAllVersions(dest); - for (VarVersionPaar varpaar : setVars) { + for (VarVersionPair varpaar : setVars) { VarVersionNode node = ssau.getSsuversions().nodes.getWithKey(varpaar); for (Iterator>> itent = node.live.entryList().iterator(); itent.hasNext(); ) { @@ -279,9 +219,11 @@ public class StackVarsProcessor { } } - private int[] iterateExprent(List lstExprents, int index, Exprent next, HashMap mapVarValues, SSAUConstructorSparseEx ssau) { - + private int[] iterateExprent(List lstExprents, + int index, + Exprent next, + Map mapVarValues, + SSAUConstructorSparseEx ssau) { Exprent exprent = lstExprents.get(index); int changed = 0; @@ -327,9 +269,9 @@ public class StackVarsProcessor { return new int[]{-1, changed}; } - VarVersionPaar leftpaar = new VarVersionPaar(left); + VarVersionPair leftpaar = new VarVersionPair(left); - List usedVers = new ArrayList(); + List usedVers = new ArrayList<>(); boolean notdom = getUsedVersions(ssau, leftpaar, usedVers); if (!notdom && usedVers.isEmpty()) { @@ -338,8 +280,8 @@ public class StackVarsProcessor { if (right.type == Exprent.EXPRENT_NEW) { // new Object(); permitted NewExprent nexpr = (NewExprent)right; - if (nexpr.isAnonymous() || nexpr.getNewtype().arraydim > 0 - || nexpr.getNewtype().type != CodeConstants.TYPE_OBJECT) { + if (nexpr.isAnonymous() || nexpr.getNewType().arrayDim > 0 + || nexpr.getNewType().type != CodeConstants.TYPE_OBJECT) { return new int[]{-1, changed}; } } @@ -368,14 +310,14 @@ public class StackVarsProcessor { return new int[]{-1, changed}; } - HashMap> mapVars = getAllVarVersions(leftpaar, right, ssau); + Map> mapVars = getAllVarVersions(leftpaar, right, ssau); boolean isSelfReference = mapVars.containsKey(leftpaar.var); if (isSelfReference && notdom) { return new int[]{-1, changed}; } - HashSet setNextVars = next == null ? null : getAllVersions(next); + Set setNextVars = next == null ? null : getAllVersions(next); // FIXME: fix the entire method! if (right.type != Exprent.EXPRENT_CONST && @@ -383,7 +325,7 @@ public class StackVarsProcessor { setNextVars != null && mapVars.containsKey(leftpaar.var)) { for (VarVersionNode usedvar : usedVers) { - if (!setNextVars.contains(new VarVersionPaar(usedvar.var, usedvar.version))) { + if (!setNextVars.contains(new VarVersionPair(usedvar.var, usedvar.version))) { return new int[]{-1, changed}; } } @@ -394,11 +336,10 @@ public class StackVarsProcessor { boolean vernotreplaced = false; boolean verreplaced = false; - - HashSet setTempUsedVers = new HashSet(); + Set setTempUsedVers = new HashSet<>(); for (VarVersionNode usedvar : usedVers) { - VarVersionPaar usedver = new VarVersionPaar(usedvar.var, usedvar.version); + VarVersionPair usedver = new VarVersionPair(usedvar.var, usedvar.version); if (isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) && (right.type == Exprent.EXPRENT_CONST || right.type == Exprent.EXPRENT_VAR || right.type == Exprent.EXPRENT_FIELD || setNextVars == null || setNextVars.contains(usedver))) { @@ -415,7 +356,7 @@ public class StackVarsProcessor { return new int[]{-1, changed}; } else { - for (VarVersionPaar usedver : setTempUsedVers) { + for (VarVersionPair usedver : setTempUsedVers) { Exprent copy = right.copy(); if (right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) { ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id)); @@ -438,17 +379,16 @@ public class StackVarsProcessor { } } - private static HashSet getAllVersions(Exprent exprent) { + private static Set getAllVersions(Exprent exprent) { + Set res = new HashSet<>(); - HashSet res = new HashSet(); - - List listTemp = new ArrayList(exprent.getAllExprents(true)); + List listTemp = new ArrayList<>(exprent.getAllExprents(true)); listTemp.add(exprent); for (Exprent expr : listTemp) { if (expr.type == Exprent.EXPRENT_VAR) { VarExprent var = (VarExprent)expr; - res.add(new VarVersionPaar(var)); + res.add(new VarVersionPair(var)); } } @@ -458,9 +398,8 @@ public class StackVarsProcessor { private static Object[] iterateChildExprent(Exprent exprent, Exprent parent, Exprent next, - HashMap mapVarValues, + Map mapVarValues, SSAUConstructorSparseEx ssau) { - boolean changed = false; for (Exprent expr : exprent.getAllExprents()) { @@ -487,7 +426,7 @@ public class StackVarsProcessor { } } - Exprent dest = isReplaceableVar(exprent, mapVarValues, ssau); + Exprent dest = isReplaceableVar(exprent, mapVarValues); if (dest != null) { return new Object[]{dest, true, true}; } @@ -511,7 +450,7 @@ public class StackVarsProcessor { boolean isHeadSynchronized = false; if (next == null && parent.type == Exprent.EXPRENT_MONITOR) { MonitorExprent monexpr = (MonitorExprent)parent; - if (monexpr.getMontype() == MonitorExprent.MONITOR_ENTER && exprent.equals(monexpr.getValue())) { + if (monexpr.getMonType() == MonitorExprent.MONITOR_ENTER && exprent.equals(monexpr.getValue())) { isHeadSynchronized = true; } } @@ -521,9 +460,9 @@ public class StackVarsProcessor { return new Object[]{null, changed, false}; } - VarVersionPaar leftpaar = new VarVersionPaar(left); + VarVersionPair leftpaar = new VarVersionPair(left); - List usedVers = new ArrayList(); + List usedVers = new ArrayList<>(); boolean notdom = getUsedVersions(ssau, leftpaar, usedVers); if (!notdom && usedVers.isEmpty()) { @@ -541,26 +480,24 @@ public class StackVarsProcessor { return new Object[]{null, changed, false}; } - HashMap> mapVars = getAllVarVersions(leftpaar, right, ssau); - + Map> mapVars = getAllVarVersions(leftpaar, right, ssau); if (mapVars.containsKey(leftpaar.var) && notdom) { return new Object[]{null, changed, false}; } - mapVars.remove(leftpaar.var); - HashSet setAllowedVars = getAllVersions(parent); + Set setAllowedVars = getAllVersions(parent); if (next != null) { setAllowedVars.addAll(getAllVersions(next)); } boolean vernotreplaced = false; - HashSet setTempUsedVers = new HashSet(); + Set setTempUsedVers = new HashSet<>(); for (VarVersionNode usedvar : usedVers) { - VarVersionPaar usedver = new VarVersionPaar(usedvar.var, usedvar.version); + VarVersionPair usedver = new VarVersionPair(usedvar.var, usedvar.version); if (isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) && (right.type == Exprent.EXPRENT_VAR || setAllowedVars.contains(usedver))) { @@ -572,8 +509,7 @@ public class StackVarsProcessor { } if (!notdom && !vernotreplaced) { - - for (VarVersionPaar usedver : setTempUsedVers) { + for (VarVersionPair usedver : setTempUsedVers) { Exprent copy = right.copy(); if (right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) { ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id)); @@ -589,20 +525,17 @@ public class StackVarsProcessor { return new Object[]{null, changed, false}; } - private static boolean getUsedVersions(SSAUConstructorSparseEx ssa, VarVersionPaar var, List res) { - + private static boolean getUsedVersions(SSAUConstructorSparseEx ssa, VarVersionPair var, List res) { VarVersionsGraph ssuversions = ssa.getSsuversions(); VarVersionNode varnode = ssuversions.nodes.getWithKey(var); - HashSet setVisited = new HashSet(); + Set setVisited = new HashSet<>(); + Set setNotDoms = new HashSet<>(); - HashSet setNotDoms = new HashSet(); - - LinkedList stack = new LinkedList(); + LinkedList stack = new LinkedList<>(); stack.add(varnode); while (!stack.isEmpty()) { - VarVersionNode nd = stack.remove(0); setVisited.add(nd); @@ -638,11 +571,10 @@ public class StackVarsProcessor { return !setNotDoms.isEmpty(); } - private static boolean isVersionToBeReplaced(VarVersionPaar usedvar, - HashMap> mapVars, + private static boolean isVersionToBeReplaced(VarVersionPair usedvar, + Map> mapVars, SSAUConstructorSparseEx ssau, - VarVersionPaar leftpaar) { - + VarVersionPair leftpaar) { VarVersionsGraph ssuversions = ssau.getSsuversions(); SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(usedvar); @@ -657,21 +589,21 @@ public class StackVarsProcessor { return false; } - for (Entry> ent : mapVars.entrySet()) { + for (Entry> ent : mapVars.entrySet()) { FastSparseSet liveverset = mapLiveVars.get(ent.getKey()); if (liveverset == null) { return false; } - HashSet domset = new HashSet(); - for (VarVersionPaar verpaar : ent.getValue()) { + Set domset = new HashSet<>(); + for (VarVersionPair verpaar : ent.getValue()) { domset.add(ssuversions.nodes.getWithKey(verpaar)); } boolean isdom = false; for (Integer livever : liveverset) { - VarVersionNode node = ssuversions.nodes.getWithKey(new VarVersionPaar(ent.getKey().intValue(), livever.intValue())); + VarVersionNode node = ssuversions.nodes.getWithKey(new VarVersionPair(ent.getKey().intValue(), livever.intValue())); if (ssuversions.isDominatorSet(node, domset)) { isdom = true; @@ -687,11 +619,10 @@ public class StackVarsProcessor { return true; } - private static HashMap> getAllVarVersions(VarVersionPaar leftvar, - Exprent exprent, - SSAUConstructorSparseEx ssau) { - - HashMap> map = new HashMap>(); + private static Map> getAllVarVersions(VarVersionPair leftvar, + Exprent exprent, + SSAUConstructorSparseEx ssau) { + Map> map = new HashMap<>(); SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(leftvar); List lst = exprent.getAllExprents(true); @@ -702,9 +633,9 @@ public class StackVarsProcessor { int varindex = ((VarExprent)expr).getIndex(); if (leftvar.var != varindex) { if (mapLiveVars.containsKey(varindex)) { - HashSet verset = new HashSet(); + Set verset = new HashSet<>(); for (Integer vers : mapLiveVars.get(varindex)) { - verset.add(new VarVersionPaar(varindex, vers.intValue())); + verset.add(new VarVersionPair(varindex, vers.intValue())); } map.put(varindex, verset); } @@ -720,9 +651,9 @@ public class StackVarsProcessor { if (ssau.getMapFieldVars().containsKey(expr.id)) { int varindex = ssau.getMapFieldVars().get(expr.id); if (mapLiveVars.containsKey(varindex)) { - HashSet verset = new HashSet(); + Set verset = new HashSet<>(); for (Integer vers : mapLiveVars.get(varindex)) { - verset.add(new VarVersionPaar(varindex, vers.intValue())); + verset.add(new VarVersionPair(varindex, vers.intValue())); } map.put(varindex, verset); } @@ -732,4 +663,4 @@ public class StackVarsProcessor { return map; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java b/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java index 26d427b..c481c0a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; @@ -21,9 +7,6 @@ import java.util.ArrayList; import java.util.List; public class StatEdge { - - public static final int TYPE_ALL = 0xFF; - public static final int TYPE_REGULAR = 1; public static final int TYPE_EXCEPTION = 2; public static final int TYPE_BREAK = 4; @@ -66,7 +49,7 @@ public class StatEdge { public StatEdge(Statement source, Statement destination, List exceptions) { this(TYPE_EXCEPTION, source, destination); if (exceptions != null) { - this.exceptions = new ArrayList(exceptions); + this.exceptions = new ArrayList<>(exceptions); } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java index 23f778d..fcea19f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java @@ -1,99 +1,24 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.util.ListStack; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; - -// -------------------------------------------------------------------- -// Algorithm -// -------------------------------------------------------------------- -// DFS(G) -// { -// make a new vertex x with edges x->v for all v -// initialize a counter N to zero -// initialize list L to empty -// build directed tree T, initially a single vertex {x} -// visit(x) -// } -// -// visit(p) -// { -// add p to L -// dfsnum(p) = N -// increment N -// low(p) = dfsnum(p) -// for each edge p->q -// if q is not already in T -// { -// add p->q to T -// visit(q) -// low(p) = min(low(p), low(q)) -// } else low(p) = min(low(p), dfsnum(q)) -// -// if low(p)=dfsnum(p) -// { -// output "component:" -// repeat -// remove last element v from L -// output v -// remove v from G -// until v=p -// } -// } -// -------------------------------------------------------------------- +import java.util.*; public class StrongConnectivityHelper { + private final List> components; + private final Set setProcessed; private ListStack lstack; - private int ncounter; - - private HashSet tset; - private HashMap dfsnummap; - private HashMap lowmap; - - private List> components; - - private HashSet setProcessed; - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public StrongConnectivityHelper() { - } + private Set tset; + private Map dfsnummap; + private Map lowmap; public StrongConnectivityHelper(Statement stat) { - findComponents(stat); - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public List> findComponents(Statement stat) { - - components = new ArrayList>(); - setProcessed = new HashSet(); + components = new ArrayList<>(); + setProcessed = new HashSet<>(); visitTree(stat.getFirst()); @@ -109,44 +34,14 @@ public class StrongConnectivityHelper { visitTree(st); } } - - return components; } - public static boolean isExitComponent(List lst) { - - HashSet set = new HashSet(); - for (Statement stat : lst) { - set.addAll(stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD)); - } - set.removeAll(lst); - - return (set.size() == 0); - } - - public static List getExitReps(List> lst) { - - List res = new ArrayList(); - - for (List comp : lst) { - if (isExitComponent(comp)) { - res.add(comp.get(0)); - } - } - - return res; - } - - // ***************************************************************************** - // private methods - // ***************************************************************************** - private void visitTree(Statement stat) { - lstack = new ListStack(); + lstack = new ListStack<>(); ncounter = 0; - tset = new HashSet(); - dfsnummap = new HashMap(); - lowmap = new HashMap(); + tset = new HashSet<>(); + dfsnummap = new HashMap<>(); + lowmap = new HashMap<>(); visit(stat); @@ -155,7 +50,6 @@ public class StrongConnectivityHelper { } private void visit(Statement stat) { - lstack.push(stat); dfsnummap.put(stat, ncounter); lowmap.put(stat, ncounter); @@ -164,8 +58,7 @@ public class StrongConnectivityHelper { List lstSuccs = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD); // TODO: set? lstSuccs.removeAll(setProcessed); - for (int i = 0; i < lstSuccs.size(); i++) { - Statement succ = lstSuccs.get(i); + for (Statement succ : lstSuccs) { int secvalue; if (tset.contains(succ)) { @@ -181,7 +74,7 @@ public class StrongConnectivityHelper { if (lowmap.get(stat).intValue() == dfsnummap.get(stat).intValue()) { - List lst = new ArrayList(); + List lst = new ArrayList<>(); Statement v; do { v = lstack.pop(); @@ -192,16 +85,31 @@ public class StrongConnectivityHelper { } } + public static boolean isExitComponent(List lst) { + Set set = new HashSet<>(); + for (Statement stat : lst) { + set.addAll(stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD)); + } + for (Statement stat : lst) { + set.remove(stat); + } - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** + return (set.size() == 0); + } + + public static List getExitReps(List> lst) { + List res = new ArrayList<>(); + + for (List comp : lst) { + if (isExitComponent(comp)) { + res.add(comp.get(0)); + } + } + + return res; + } public List> getComponents() { return components; } - - public void setComponents(List> components) { - this.components = components; - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java new file mode 100644 index 0000000..1e08a6d --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java @@ -0,0 +1,80 @@ +// 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; +import org.jetbrains.java.decompiler.main.ClassesProcessor; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import org.jetbrains.java.decompiler.main.rels.MethodWrapper; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class SwitchHelper { + public static void simplify(SwitchStatement switchStatement) { + SwitchExprent switchExprent = (SwitchExprent)switchStatement.getHeadexprent(); + Exprent value = switchExprent.getValue(); + if (isEnumArray(value)) { + List> caseValues = switchStatement.getCaseValues(); + Map mapping = new HashMap<>(caseValues.size()); + ArrayExprent array = (ArrayExprent)value; + FieldExprent arrayField = (FieldExprent)array.getArray(); + ClassesProcessor.ClassNode classNode = + DecompilerContext.getClassProcessor().getMapRootClasses().get(arrayField.getClassname()); + if (classNode != null) { + MethodWrapper wrapper = classNode.getWrapper().getMethodWrapper(CodeConstants.CLINIT_NAME, "()V"); + if (wrapper != null && wrapper.root != null) { + wrapper.getOrBuildGraph().iterateExprents(exprent -> { + if (exprent instanceof AssignmentExprent) { + AssignmentExprent assignment = (AssignmentExprent)exprent; + Exprent left = assignment.getLeft(); + if (left.type == Exprent.EXPRENT_ARRAY && ((ArrayExprent)left).getArray().equals(arrayField)) { + mapping.put(assignment.getRight(), ((InvocationExprent)((ArrayExprent)left).getIndex()).getInstance()); + } + } + return 0; + }); + } + } + + List> realCaseValues = new ArrayList<>(caseValues.size()); + for (List caseValue : caseValues) { + List values = new ArrayList<>(caseValue.size()); + realCaseValues.add(values); + for (Exprent exprent : caseValue) { + if (exprent == null) { + values.add(null); + } + else { + Exprent realConst = mapping.get(exprent); + if (realConst == null) { + DecompilerContext.getLogger() + .writeMessage("Unable to simplify switch on enum: " + exprent + " not found, available: " + mapping, + IFernflowerLogger.Severity.ERROR); + return; + } + values.add(realConst.copy()); + } + } + } + caseValues.clear(); + caseValues.addAll(realCaseValues); + switchExprent.replaceExprent(value, ((InvocationExprent)array.getIndex()).getInstance().copy()); + } + } + + private static boolean isEnumArray(Exprent exprent) { + if (exprent instanceof ArrayExprent) { + Exprent field = ((ArrayExprent)exprent).getArray(); + Exprent index = ((ArrayExprent)exprent).getIndex(); + return field instanceof FieldExprent && + (((FieldExprent)field).getName().startsWith("$SwitchMap") || + (index instanceof InvocationExprent && ((InvocationExprent)index).getName().equals("ordinal"))); + } + return false; + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java index aeab0d2..0d908a2 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.decompose; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; @@ -23,9 +9,9 @@ import java.util.List; public class DominatorEngine { - private Statement statement; + private final Statement statement; - private VBStyleCollection colOrderedIDoms = new VBStyleCollection(); + private final VBStyleCollection colOrderedIDoms = new VBStyleCollection<>(); public DominatorEngine(Statement statement) { 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 c11d5bf..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,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -24,19 +10,19 @@ import java.util.Map.Entry; public class DominatorTreeExceptionFilter { - private Statement statement; + private final Statement statement; // idom, nodes - private Map> mapTreeBranches = new HashMap>(); + private final Map> mapTreeBranches = new HashMap<>(); // handler, range nodes - private Map> mapExceptionRanges = new HashMap>(); + private final Map> mapExceptionRanges = new HashMap<>(); // handler, head dom - private Map mapExceptionDoms = new HashMap(); + private Map mapExceptionDoms = new HashMap<>(); // statement, handler, exit nodes - private Map> mapExceptionRangeUniqueExit = new HashMap>(); + private final Map> mapExceptionRangeUniqueExit = new HashMap<>(); private DominatorEngine domEngine; @@ -45,7 +31,6 @@ public class DominatorTreeExceptionFilter { } public void initialize() { - domEngine = new DominatorEngine(statement); domEngine.initialize(); @@ -61,12 +46,11 @@ public class DominatorTreeExceptionFilter { } public boolean acceptStatementPair(Integer head, Integer exit) { - Map filter = mapExceptionRangeUniqueExit.get(head); for (Entry entry : filter.entrySet()) { if (!head.equals(mapExceptionDoms.get(entry.getKey()))) { Integer filterExit = entry.getValue(); - if (filterExit.intValue() == -1 || !filterExit.equals(exit)) { + if (filterExit == -1 || !filterExit.equals(exit)) { return false; } } @@ -76,19 +60,13 @@ public class DominatorTreeExceptionFilter { } private void buildDominatorTree() { - VBStyleCollection orderedIDoms = domEngine.getOrderedIDoms(); List lstKeys = orderedIDoms.getLstKeys(); for (int index = lstKeys.size() - 1; index >= 0; index--) { Integer key = lstKeys.get(index); Integer idom = orderedIDoms.get(index); - - Set set = mapTreeBranches.get(idom); - if (set == null) { - mapTreeBranches.put(idom, set = new HashSet()); - } - set.add(key); + mapTreeBranches.computeIfAbsent(idom, k -> new HashSet<>()).add(key); } Integer firstid = statement.getFirst().id; @@ -96,12 +74,11 @@ public class DominatorTreeExceptionFilter { } private void buildExceptionRanges() { - for (Statement stat : statement.getStats()) { List lstPreds = stat.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD); if (!lstPreds.isEmpty()) { - Set set = new HashSet(); + Set set = new HashSet<>(); for (Statement st : lstPreds) { set.add(st.id); @@ -115,8 +92,7 @@ public class DominatorTreeExceptionFilter { } private Map buildExceptionDoms(Integer id) { - - Map map = new HashMap(); + Map map = new HashMap<>(); Set children = mapTreeBranches.get(id); if (children != null) { @@ -137,34 +113,29 @@ public class DominatorTreeExceptionFilter { return map; } - private void buildFilter(Integer id) { - - Map map = new HashMap(); + Map map = new HashMap<>(); Set children = mapTreeBranches.get(id); if (children != null) { for (Integer childid : children) { - buildFilter(childid); Map mapChild = mapExceptionRangeUniqueExit.get(childid); - for (Entry> entry : mapExceptionRanges.entrySet()) { - Integer handler = entry.getKey(); Set range = entry.getValue(); if (range.contains(id)) { - - Integer exit = null; - + Integer exit; if (!range.contains(childid)) { exit = childid; } + else if (map.containsKey(handler)) { + exit = -1; + } else { - // exit = map.containsKey(handler)?-1:mapChild.get(handler); FIXME: Eclipse bug? - exit = map.containsKey(handler) ? new Integer(-1) : mapChild.get(handler); + exit = mapChild.get(handler); } if (exit != null) { @@ -181,4 +152,4 @@ public class DominatorTreeExceptionFilter { public DominatorEngine getDomEngine() { return domEngine; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java index f029016..71ef24b 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.decompose; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; @@ -28,9 +14,9 @@ public class FastExtendedPostdominanceHelper { private List lstReversePostOrderList; - private HashMap> mapSupportPoints = new HashMap>(); + private HashMap> mapSupportPoints = new HashMap<>(); - private HashMap> mapExtPostdominators = new HashMap>(); + private final HashMap> mapExtPostdominators = new HashMap<>(); private Statement statement; @@ -40,11 +26,11 @@ public class FastExtendedPostdominanceHelper { this.statement = statement; - HashSet set = new HashSet(); + HashSet set = new HashSet<>(); for (Statement st : statement.getStats()) { set.add(st.id); } - this.factory = new FastFixedSetFactory(set); + this.factory = new FastFixedSetFactory<>(set); lstReversePostOrderList = statement.getReversePostOrderList(); @@ -65,8 +51,9 @@ public class FastExtendedPostdominanceHelper { filterOnDominance(filter); - HashMap> res = new HashMap>(); - for (Entry> entry : mapExtPostdominators.entrySet()) { + Set>> entries = mapExtPostdominators.entrySet(); + HashMap> res = new HashMap<>(entries.size()); + for (Entry> entry : entries) { res.put(entry.getKey(), entry.getValue().toPlainSet()); } @@ -78,17 +65,19 @@ public class FastExtendedPostdominanceHelper { DominatorEngine engine = filter.getDomEngine(); - for (Integer head : new HashSet(mapExtPostdominators.keySet())) { + for (Integer head : new HashSet<>(mapExtPostdominators.keySet())) { FastFixedSet setPostdoms = mapExtPostdominators.get(head); - LinkedList stack = new LinkedList(); - LinkedList> stackPath = new LinkedList>(); + LinkedList stack = new LinkedList<>(); + LinkedList> stackPath = new LinkedList<>(); stack.add(statement.getStats().getWithKey(head)); stackPath.add(factory.spawnEmptySet()); - Set setVisited = new HashSet(); + Set setVisited = new HashSet<>(); + + setVisited.add(stack.getFirst()); while (!stack.isEmpty()) { @@ -103,28 +92,21 @@ public class FastExtendedPostdominanceHelper { continue; } - setVisited.add(stat); - - int domflag = 0; - - for (Iterator it = setPostdoms.iterator(); it.hasNext(); ) { - Integer post = it.next(); - - if (!path.contains(post)) { - if (domflag == 0) { - domflag = engine.isDominator(stat.id, head) ? 2 : 1; - } - - if (domflag == 1) { // not a dominator - it.remove(); - } - } + if(!engine.isDominator(stat.id, head)) { + setPostdoms.complement(path); + continue; } for (StatEdge edge : stat.getSuccessorEdges(StatEdge.TYPE_REGULAR)) { - if (!setVisited.contains(edge.getDestination())) { - stack.add(edge.getDestination()); + + Statement edge_destination = edge.getDestination(); + + if(!setVisited.contains(edge_destination)) { + + stack.add(edge_destination); stackPath.add(path.getCopy()); + + setVisited.add(edge_destination); } } } @@ -135,12 +117,8 @@ public class FastExtendedPostdominanceHelper { } } - private void filterOnExceptionRanges(DominatorTreeExceptionFilter filter) { - - - for (Integer head : new HashSet(mapExtPostdominators.keySet())) { - + for (Integer head : new HashSet<>(mapExtPostdominators.keySet())) { FastFixedSet set = mapExtPostdominators.get(head); for (Iterator it = set.iterator(); it.hasNext(); ) { if (!filter.acceptStatementPair(head, it.next())) { @@ -153,64 +131,59 @@ public class FastExtendedPostdominanceHelper { } } - private void removeErroneousNodes() { - - mapSupportPoints = new HashMap>(); + mapSupportPoints = new HashMap<>(); calcReachabilitySuppPoints(StatEdge.TYPE_REGULAR); - iterateReachability(new IReachabilityAction() { - public boolean action(Statement node, HashMap> mapSets) { + iterateReachability((node, mapSets) -> { + Integer nodeid = node.id; - Integer nodeid = node.id; + FastFixedSet setReachability = mapSets.get(nodeid); + List> lstPredSets = new ArrayList<>(); - FastFixedSet setReachability = mapSets.get(nodeid); - List> lstPredSets = new ArrayList>(); - - for (StatEdge prededge : node.getPredecessorEdges(StatEdge.TYPE_REGULAR)) { - FastFixedSet setPred = mapSets.get(prededge.getSource().id); - if (setPred == null) { - setPred = mapSupportPoints.get(prededge.getSource().id); - } - - // setPred cannot be empty as it is a reachability set - lstPredSets.add(setPred); + for (StatEdge prededge : node.getPredecessorEdges(StatEdge.TYPE_REGULAR)) { + FastFixedSet setPred = mapSets.get(prededge.getSource().id); + if (setPred == null) { + setPred = mapSupportPoints.get(prededge.getSource().id); } - for (Integer id : setReachability.toPlainSet()) { + // setPred cannot be empty as it is a reachability set + lstPredSets.add(setPred); + } - FastFixedSet setReachabilityCopy = setReachability.getCopy(); + for (Integer id : setReachability) { - FastFixedSet setIntersection = factory.spawnEmptySet(); - boolean isIntersectionInitialized = false; + FastFixedSet setReachabilityCopy = setReachability.getCopy(); - for (FastFixedSet predset : lstPredSets) { - if (predset.contains(id)) { - if (!isIntersectionInitialized) { - setIntersection.union(predset); - isIntersectionInitialized = true; - } - else { - setIntersection.intersection(predset); - } + FastFixedSet setIntersection = factory.spawnEmptySet(); + boolean isIntersectionInitialized = false; + + for (FastFixedSet predset : lstPredSets) { + if (predset.contains(id)) { + if (!isIntersectionInitialized) { + setIntersection.union(predset); + isIntersectionInitialized = true; + } + else { + setIntersection.intersection(predset); } } - - if (nodeid != id.intValue()) { - setIntersection.add(nodeid); - } - else { - setIntersection.remove(nodeid); - } - - setReachabilityCopy.complement(setIntersection); - - mapExtPostdominators.get(id).complement(setReachabilityCopy); } - return false; + if (nodeid != id.intValue()) { + setIntersection.add(nodeid); + } + else { + setIntersection.remove(nodeid); + } + + setReachabilityCopy.complement(setIntersection); + + mapExtPostdominators.get(id).complement(setReachabilityCopy); } + + return false; }, StatEdge.TYPE_REGULAR); // exception handlers cannot be postdominator nodes @@ -233,9 +206,7 @@ public class FastExtendedPostdominanceHelper { } } - private void calcDefaultReachableSets() { - int edgetype = StatEdge.TYPE_REGULAR | StatEdge.TYPE_EXCEPTION; calcReachabilitySuppPoints(edgetype); @@ -244,53 +215,43 @@ public class FastExtendedPostdominanceHelper { mapExtPostdominators.put(stat.id, factory.spawnEmptySet()); } - iterateReachability(new IReachabilityAction() { - public boolean action(Statement node, HashMap> mapSets) { + iterateReachability((node, mapSets) -> { + Integer nodeid = node.id; + FastFixedSet setReachability = mapSets.get(nodeid); - Integer nodeid = node.id; - FastFixedSet setReachability = mapSets.get(nodeid); - - for (Integer id : setReachability.toPlainSet()) { - mapExtPostdominators.get(id).add(nodeid); - } - - return false; + for (Integer id : setReachability) { + mapExtPostdominators.get(id).add(nodeid); } + + return false; }, edgetype); } - private void calcReachabilitySuppPoints(final int edgetype) { + iterateReachability((node, mapSets) -> { + // consider to be a support point + for (StatEdge sucedge : node.getAllSuccessorEdges()) { + if ((sucedge.getType() & edgetype) != 0) { + if (mapSets.containsKey(sucedge.getDestination().id)) { + FastFixedSet setReachability = mapSets.get(node.id); - iterateReachability(new IReachabilityAction() { - public boolean action(Statement node, HashMap> mapSets) { - - // consider to be a support point - for (StatEdge sucedge : node.getAllSuccessorEdges()) { - if ((sucedge.getType() & edgetype) != 0) { - if (mapSets.containsKey(sucedge.getDestination().id)) { - FastFixedSet setReachability = mapSets.get(node.id); - - if (!InterpreterUtil.equalObjects(setReachability, mapSupportPoints.get(node.id))) { - mapSupportPoints.put(node.id, setReachability); - return true; - } + if (!InterpreterUtil.equalObjects(setReachability, mapSupportPoints.get(node.id))) { + mapSupportPoints.put(node.id, setReachability); + return true; } } } - - return false; } + + return false; }, edgetype); } private void iterateReachability(IReachabilityAction action, int edgetype) { - while (true) { - boolean iterate = false; - HashMap> mapSets = new HashMap>(); + HashMap> mapSets = new HashMap<>(); for (Statement stat : lstReversePostOrderList) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java index c6eb357..01a32f9 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.decompose; import org.jetbrains.java.decompiler.util.VBStyleCollection; @@ -22,9 +8,9 @@ import java.util.Set; public class GenericDominatorEngine { - private IGraph graph; + private final IGraph graph; - private VBStyleCollection colOrderedIDoms = new VBStyleCollection(); + private final VBStyleCollection colOrderedIDoms = new VBStyleCollection<>(); private Set setRoots; @@ -126,10 +112,6 @@ public class GenericDominatorEngine { } } - public VBStyleCollection getOrderedIDoms() { - return colOrderedIDoms; - } - public boolean isDominator(IGraphNode node, IGraphNode dom) { while (!node.equals(dom)) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java index faaf4c6..6ad7439 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.decompose; import java.util.List; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java index 19d59b5..8dd357b 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.decompose; import java.util.List; 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 7614807..c0a7769 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -22,6 +8,8 @@ import org.jetbrains.java.decompiler.code.SimpleInstructionSequence; import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.modules.decompiler.decompose.GenericDominatorEngine; import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraph; import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode; @@ -30,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; @@ -48,7 +36,7 @@ public class ExceptionDeobfuscator { public static void restorePopRanges(ControlFlowGraph graph) { - List lstRanges = new ArrayList(); + List lstRanges = new ArrayList<>(); // aggregate ranges for (ExceptionRangeCFG range : graph.getExceptions()) { @@ -63,7 +51,7 @@ public class ExceptionDeobfuscator { if (!found) { // doesn't matter, which range chosen - lstRanges.add(new Range(range.getHandler(), range.getUniqueExceptionsString(), new HashSet(range.getProtectedRange()), range)); + lstRanges.add(new Range(range.getHandler(), range.getUniqueExceptionsString(), new HashSet<>(range.getProtectedRange()), range)); } } @@ -81,13 +69,13 @@ public class ExceptionDeobfuscator { if (firstinstr.opcode == CodeConstants.opc_pop || firstinstr.opcode == CodeConstants.opc_astore) { - Set setrange = new HashSet(range.protectedRange); + Set setrange = new HashSet<>(range.protectedRange); for (Range range_super : lstRanges) { // finally or strict superset if (range != range_super) { - Set setrange_super = new HashSet(range_super.protectedRange); + Set setrange_super = new HashSet<>(range_super.protectedRange); if (!setrange.contains(range_super.handler) && !setrange_super.contains(handler) && (range_super.uniqueStr == null || setrange_super.containsAll(setrange))) { @@ -113,7 +101,7 @@ public class ExceptionDeobfuscator { graph.getBlocks().addWithKey(newblock, newblock.id); - List lstTemp = new ArrayList(); + List lstTemp = new ArrayList<>(); lstTemp.addAll(handler.getPreds()); lstTemp.addAll(handler.getPredExceptions()); @@ -159,7 +147,7 @@ public class ExceptionDeobfuscator { public static void insertEmptyExceptionHandlerBlocks(ControlFlowGraph graph) { - Set setVisited = new HashSet(); + Set setVisited = new HashSet<>(); for (ExceptionRangeCFG range : graph.getExceptions()) { BasicBlock handler = range.getHandler(); @@ -172,9 +160,8 @@ public class ExceptionDeobfuscator { BasicBlock emptyblock = new BasicBlock(++graph.last_id); graph.getBlocks().addWithKey(emptyblock, emptyblock.id); - List lstTemp = new ArrayList(); // only exception predecessors considered - lstTemp.addAll(handler.getPredExceptions()); + List lstTemp = new ArrayList<>(handler.getPredExceptions()); // replace predecessors for (BasicBlock pred : lstTemp) { @@ -226,12 +213,14 @@ public class ExceptionDeobfuscator { public static void removeCircularRanges(final ControlFlowGraph graph) { GenericDominatorEngine engine = new GenericDominatorEngine(new IGraph() { + @Override public List getReversePostOrderList() { return graph.getReversePostOrder(); } + @Override public Set getRoots() { - return new HashSet(Arrays.asList(new IGraphNode[]{graph.getFirst()})); + return new HashSet<>(Collections.singletonList(graph.getFirst())); } }); @@ -246,7 +235,7 @@ public class ExceptionDeobfuscator { if (rangeList.contains(handler)) { // TODO: better removing strategy - List lstRemBlocks = getReachableBlocksRestricted(range, engine); + List lstRemBlocks = getReachableBlocksRestricted(range.getHandler(), range, engine); if (lstRemBlocks.size() < rangeList.size() || rangeList.size() == 1) { for (BasicBlock block : lstRemBlocks) { @@ -262,25 +251,24 @@ public class ExceptionDeobfuscator { } } - private static List getReachableBlocksRestricted(ExceptionRangeCFG range, GenericDominatorEngine engine) { + private static List getReachableBlocksRestricted(BasicBlock start, ExceptionRangeCFG range, GenericDominatorEngine engine) { - List lstRes = new ArrayList(); + List lstRes = new ArrayList<>(); - LinkedList stack = new LinkedList(); - Set setVisited = new HashSet(); + LinkedList stack = new LinkedList<>(); + Set setVisited = new HashSet<>(); - BasicBlock handler = range.getHandler(); - stack.addFirst(handler); + stack.addFirst(start); while (!stack.isEmpty()) { BasicBlock block = stack.removeFirst(); setVisited.add(block); - if (range.getProtectedRange().contains(block) && engine.isDominator(block, handler)) { + if (range.getProtectedRange().contains(block) && engine.isDominator(block, start)) { lstRes.add(block); - List lstSuccs = new ArrayList(block.getSuccs()); + List lstSuccs = new ArrayList<>(block.getSuccs()); lstSuccs.addAll(block.getSuccExceptions()); for (BasicBlock succ : lstSuccs) { @@ -294,23 +282,17 @@ public class ExceptionDeobfuscator { return lstRes; } - public static boolean hasObfuscatedExceptions(ControlFlowGraph graph) { - - Map> mapRanges = new HashMap>(); + Map> mapRanges = new HashMap<>(); for (ExceptionRangeCFG range : graph.getExceptions()) { - Set set = mapRanges.get(range.getHandler()); - if (set == null) { - mapRanges.put(range.getHandler(), set = new HashSet()); - } - set.addAll(range.getProtectedRange()); + mapRanges.computeIfAbsent(range.getHandler(), k -> new HashSet<>()).addAll(range.getProtectedRange()); } for (Entry> ent : mapRanges.entrySet()) { - Set setEntries = new HashSet(); + Set setEntries = new HashSet<>(); for (BasicBlock block : ent.getValue()) { - Set setTemp = new HashSet(block.getPreds()); + Set setTemp = new HashSet<>(block.getPreds()); setTemp.removeAll(ent.getValue()); if (!setTemp.isEmpty()) { @@ -327,4 +309,140 @@ public class ExceptionDeobfuscator { return false; } -} + + public static boolean handleMultipleEntryExceptionRanges(ControlFlowGraph graph) { + GenericDominatorEngine engine = new GenericDominatorEngine(new IGraph() { + @Override + public List getReversePostOrderList() { + return graph.getReversePostOrder(); + } + + @Override + public Set getRoots() { + return new HashSet<>(Collections.singletonList(graph.getFirst())); + } + }); + + engine.initialize(); + + boolean found; + + while (true) { + found = false; + boolean splitted = false; + + for (ExceptionRangeCFG range : graph.getExceptions()) { + Set setEntries = getRangeEntries(range); + + if (setEntries.size() > 1) { // multiple-entry protected range + found = true; + + if (splitExceptionRange(range, setEntries, graph, engine)) { + splitted = true; + break; + } + } + } + + if (!splitted) { + break; + } + } + + return !found; + } + + private static Set getRangeEntries(ExceptionRangeCFG range) { + Set setEntries = new HashSet<>(); + Set setRange = new HashSet<>(range.getProtectedRange()); + + for (BasicBlock block : range.getProtectedRange()) { + Set setPreds = new HashSet<>(block.getPreds()); + setPreds.removeAll(setRange); + + if (!setPreds.isEmpty()) { + setEntries.add(block); + } + } + + return setEntries; + } + + private static boolean splitExceptionRange(ExceptionRangeCFG range, + Set setEntries, + ControlFlowGraph graph, + GenericDominatorEngine engine) { + for (BasicBlock entry : setEntries) { + List lstSubrangeBlocks = getReachableBlocksRestricted(entry, range, engine); + if (!lstSubrangeBlocks.isEmpty() && lstSubrangeBlocks.size() < range.getProtectedRange().size()) { + // add new range + ExceptionRangeCFG subRange = new ExceptionRangeCFG(lstSubrangeBlocks, range.getHandler(), range.getExceptionTypes()); + graph.getExceptions().add(subRange); + // shrink the original range + range.getProtectedRange().removeAll(lstSubrangeBlocks); + return true; + } + else { + // should not happen + DecompilerContext.getLogger().writeMessage("Inconsistency found while splitting protected range", IFernflowerLogger.Severity.WARN); + } + } + + return false; + } + + public static void insertDummyExceptionHandlerBlocks(ControlFlowGraph graph, int bytecode_version) { + Map> mapRanges = new HashMap<>(); + for (ExceptionRangeCFG range : graph.getExceptions()) { + mapRanges.computeIfAbsent(range.getHandler(), k -> new HashSet<>()).add(range); + } + + for (Entry> ent : mapRanges.entrySet()) { + BasicBlock handler = ent.getKey(); + Set ranges = ent.getValue(); + + if (ranges.size() == 1) { + continue; + } + + for (ExceptionRangeCFG range : ranges) { + + // 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); + + BasicBlock dummyBlock = new BasicBlock(++graph.last_id); + dummyBlock.setSeq(seq); + + graph.getBlocks().addWithKey(dummyBlock, dummyBlock.id); + + // only exception predecessors from this range considered + List lstPredExceptions = new ArrayList<>(handler.getPredExceptions()); + lstPredExceptions.retainAll(range.getProtectedRange()); + + // replace predecessors + for (BasicBlock pred : lstPredExceptions) { + pred.replaceSuccessor(handler, dummyBlock); + } + + // replace handler + range.setHandler(dummyBlock); + // add common exception edges + Set commonHandlers = new HashSet<>(handler.getSuccExceptions()); + for (BasicBlock pred : lstPredExceptions) { + commonHandlers.retainAll(pred.getSuccExceptions()); + } + // TODO: more sanity checks? + for (BasicBlock commonHandler : commonHandlers) { + ExceptionRangeCFG commonRange = graph.getExceptionRange(commonHandler, handler); + + dummyBlock.addSuccessorException(commonHandler); + commonRange.getProtectedRange().add(dummyBlock); + } + + dummyBlock.addSuccessor(handler); + } + } + } +} \ No newline at end of file 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 31e171e..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,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -24,22 +10,22 @@ import java.util.HashSet; import java.util.Set; -public class IrreducibleCFGDeobfuscator { +public final class IrreducibleCFGDeobfuscator { public static boolean isStatementIrreducible(Statement statement) { class Node { - public Integer id; - public Set preds = new HashSet(); - public Set succs = new HashSet(); + public final Integer id; + public final Set preds = new HashSet<>(); + public final Set succs = new HashSet<>(); - public Node(Integer id) { + Node(Integer id) { this.id = id; } } - HashMap mapNodes = new HashMap(); + HashMap mapNodes = new HashMap<>(); // checking exceptions and creating nodes for (Statement stat : statement.getStats()) { @@ -145,7 +131,7 @@ public class IrreducibleCFGDeobfuscator { StatEdge enteredge = splitnode.getPredecessorEdges(StatEdge.TYPE_REGULAR).iterator().next(); // copy the smallest statement - Statement splitcopy = copyStatement(splitnode, null, new HashMap()); + Statement splitcopy = copyStatement(splitnode, null, new HashMap<>()); initCopiedStatement(splitcopy); // insert the copy @@ -172,15 +158,13 @@ public class IrreducibleCFGDeobfuscator { private static int getStatementSize(Statement statement) { - int res = 0; + int res; if (statement.type == Statement.TYPE_BASICBLOCK) { res = ((BasicBlockStatement)statement).getBlock().getSeq().length(); } else { - for (Statement stat : statement.getStats()) { - res += getStatementSize(stat); - } + res = statement.getStats().stream().mapToInt(IrreducibleCFGDeobfuscator::getStatementSize).sum(); } return res; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java index ed3b6a4..c1d7551 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java @@ -1,17 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ package org.jetbrains.java.decompiler.modules.decompiler.exps; @@ -19,98 +7,92 @@ import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.List; - public class AnnotationExprent extends Exprent { - public static final int ANNOTATION_NORMAL = 1; public static final int ANNOTATION_MARKER = 2; public static final int ANNOTATION_SINGLE_ELEMENT = 3; + private final String className; + private final List parNames; + private final List parValues; - private String classname; - - private List parnames; - - private List parvalues; - - { - this.type = EXPRENT_ANNOTATION; - } - - public AnnotationExprent(String classname, List parnames, List parvalues) { - this.classname = classname; - this.parnames = parnames; - this.parvalues = parvalues; + public AnnotationExprent(String className, List parNames, List parValues) { + super(EXPRENT_ANNOTATION); + this.className = className; + this.parNames = parNames; + this.parValues = parValues; } @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + TextBuffer buffer = new TextBuffer(); - String new_line_separator = DecompilerContext.getNewLineSeparator(); + buffer.appendIndent(indent); + buffer.append('@'); + buffer.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(className))); - StringBuilder buffer = new StringBuilder(); - String indstr = InterpreterUtil.getIndentString(indent); + int type = getAnnotationType(); - buffer.append(indstr); - buffer.append("@"); - buffer.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(classname))); + if (type != ANNOTATION_MARKER) { + buffer.append('('); - if (!parnames.isEmpty()) { - buffer.append("("); - if (parnames.size() == 1 && "value".equals(parnames.get(0))) { - buffer.append(parvalues.get(0).toJava(indent + 1, tracer)); - } - else { - String indstr1 = InterpreterUtil.getIndentString(indent + 1); + boolean oneLiner = type == ANNOTATION_SINGLE_ELEMENT || indent < 0; - for (int i = 0; i < parnames.size(); i++) { - buffer.append(new_line_separator).append(indstr1); - buffer.append(parnames.get(i)); - buffer.append(" = "); - buffer.append(parvalues.get(i).toJava(indent + 2, tracer)); - - if (i < parnames.size() - 1) { - buffer.append(","); - } + for (int i = 0; i < parNames.size(); i++) { + if (!oneLiner) { + buffer.appendLineSeparator().appendIndent(indent + 1); + } + + if (type != ANNOTATION_SINGLE_ELEMENT) { + buffer.append(parNames.get(i)); + buffer.append(" = "); + } + + buffer.append(parValues.get(i).toJava(0, tracer)); + + if (i < parNames.size() - 1) { + buffer.append(','); } - buffer.append(new_line_separator).append(indstr); } - buffer.append(")"); + if (!oneLiner) { + buffer.appendLineSeparator().appendIndent(indent); + } + + buffer.append(')'); } - return buffer.toString(); + return buffer; + } + + public String getClassName() { + return className; } public int getAnnotationType() { - - if (parnames.isEmpty()) { + if (parNames.isEmpty()) { return ANNOTATION_MARKER; } + else if (parNames.size() == 1 && "value".equals(parNames.get(0))) { + return ANNOTATION_SINGLE_ELEMENT; + } else { - if (parnames.size() == 1 && "value".equals(parnames.get(0))) { - return ANNOTATION_SINGLE_ELEMENT; - } - else { - return ANNOTATION_NORMAL; - } + return ANNOTATION_NORMAL; } } + @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof AnnotationExprent)) return false; + if (!(o instanceof AnnotationExprent)) return false; AnnotationExprent ann = (AnnotationExprent)o; - return classname.equals(ann.classname) && - InterpreterUtil.equalLists(parnames, ann.parnames) && - InterpreterUtil.equalLists(parvalues, ann.parvalues); + return className.equals(ann.className) && + InterpreterUtil.equalLists(parNames, ann.parNames) && + InterpreterUtil.equalLists(parValues, ann.parValues); } - - public String getClassname() { - return classname; - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java index 2e81f15..753a0a6 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.exps; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; @@ -20,106 +6,102 @@ import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.ArrayList; import java.util.List; - +import java.util.Set; public class ArrayExprent extends Exprent { - private Exprent array; - private Exprent index; + private final VarType hardType; - private VarType hardtype; - - { - this.type = EXPRENT_ARRAY; - } - - public ArrayExprent(Exprent array, Exprent index, VarType hardtype) { + public ArrayExprent(Exprent array, Exprent index, VarType hardType, Set bytecodeOffsets) { + super(EXPRENT_ARRAY); this.array = array; this.index = index; - this.hardtype = hardtype; + this.hardType = hardType; + + addBytecodeOffsets(bytecodeOffsets); } + @Override public Exprent copy() { - return new ArrayExprent(array.copy(), index.copy(), hardtype); + return new ArrayExprent(array.copy(), index.copy(), hardType, bytecode); } + @Override public VarType getExprType() { - VarType exprType = array.getExprType().copy(); + VarType exprType = array.getExprType(); if (exprType.equals(VarType.VARTYPE_NULL)) { - exprType = hardtype.copy(); + return hardType.copy(); } else { - exprType.decArrayDim(); + return exprType.decreaseArrayDim(); } - - return exprType; } + @Override public int getExprentUse() { return array.getExprentUse() & index.getExprentUse() & Exprent.MULTIPLE_USES; } + @Override public CheckTypesResult checkExprTypeBounds() { CheckTypesResult result = new CheckTypesResult(); - result.addMinTypeExprent(index, VarType.VARTYPE_BYTECHAR); result.addMaxTypeExprent(index, VarType.VARTYPE_INT); - return result; } + @Override public List getAllExprents() { - List lst = new ArrayList(); + List lst = new ArrayList<>(); lst.add(array); lst.add(index); return lst; } - @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { - String res = array.toJava(indent, tracer); + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + TextBuffer res = array.toJava(indent, tracer); if (array.getPrecedence() > getPrecedence()) { // array precedence equals 0 - res = "(" + res + ")"; + res.enclose("(", ")"); } - VarType arrtype = array.getExprType(); - if (arrtype.arraydim == 0) { - VarType objarr = VarType.VARTYPE_OBJECT.copy(); - objarr.arraydim = 1; // type family does not change - - res = "((" + ExprProcessor.getCastTypeName(objarr) + ")" + res + ")"; + VarType arrType = array.getExprType(); + if (arrType.arrayDim == 0) { + VarType objArr = VarType.VARTYPE_OBJECT.resizeArrayDim(1); // type family does not change + res.enclose("((" + ExprProcessor.getCastTypeName(objArr) + ")", ")"); } tracer.addMapping(bytecode); - return res + "[" + index.toJava(indent, tracer) + "]"; + return res.append('[').append(index.toJava(indent, tracer)).append(']'); } + @Override + public void replaceExprent(Exprent oldExpr, Exprent newExpr) { + if (oldExpr == array) { + array = newExpr; + } + if (oldExpr == index) { + index = newExpr; + } + } + + @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof ArrayExprent)) return false; + if (!(o instanceof ArrayExprent)) return false; ArrayExprent arr = (ArrayExprent)o; return InterpreterUtil.equalObjects(array, arr.getArray()) && InterpreterUtil.equalObjects(index, arr.getIndex()); } - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if (oldexpr == array) { - array = newexpr; - } - - if (oldexpr == index) { - index = newexpr; - } - } - public Exprent getArray() { return array; } @@ -127,4 +109,4 @@ public class ArrayExprent extends Exprent { public Exprent getIndex() { return index; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java index f4f24ef..0dcb63d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java @@ -1,40 +1,25 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ package org.jetbrains.java.decompiler.modules.decompiler.exps; -import java.util.List; - +import org.jetbrains.java.decompiler.util.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; +import java.util.List; + public class AssertExprent extends Exprent { - private List parameters; + private final List parameters; - { - this.type = EXPRENT_ASSERT; - } - - public AssertExprent(List parameters) { + public AssertExprent(List parameters) { + super(EXPRENT_ASSERT); this.parameters = parameters; } @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { - - StringBuilder buffer = new StringBuilder(); + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + TextBuffer buffer = new TextBuffer(); buffer.append("assert "); @@ -52,6 +37,6 @@ public class AssertExprent extends Exprent { buffer.append(parameters.get(1).toJava(indent, tracer)); } - return buffer.toString(); + return buffer; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java index 75f4b75..bd5f085 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java @@ -1,111 +1,98 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ package org.jetbrains.java.decompiler.modules.decompiler.exps; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; -import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.ArrayList; import java.util.List; - +import java.util.Set; public class AssignmentExprent extends Exprent { public static final int CONDITION_NONE = -1; - private static final String[] funceq = new String[]{ - " += ", // FUNCTION_ADD - " -= ", // FUNCTION_SUB - " *= ", // FUNCTION_MUL - " /= ", // FUNCTION_DIV - " &= ", // FUNCTION_AND - " |= ", // FUNCTION_OR - " ^= ", // FUNCTION_XOR - " %= ", // FUNCTION_REM - " <<= ", // FUNCTION_SHL - " >>= ", // FUNCTION_SHR - " >>>= " // FUNCTION_USHR + private static final String[] OPERATORS = { + " += ", // FUNCTION_ADD + " -= ", // FUNCTION_SUB + " *= ", // FUNCTION_MUL + " /= ", // FUNCTION_DIV + " &= ", // FUNCTION_AND + " |= ", // FUNCTION_OR + " ^= ", // FUNCTION_XOR + " %= ", // FUNCTION_REM + " <<= ", // FUNCTION_SHL + " >>= ", // FUNCTION_SHR + " >>>= " // FUNCTION_USHR }; - private Exprent left; - private Exprent right; + private int condType = CONDITION_NONE; - private int condtype = CONDITION_NONE; - - { - this.type = EXPRENT_ASSIGNMENT; - } - - - public AssignmentExprent(Exprent left, Exprent right) { + public AssignmentExprent(Exprent left, Exprent right, Set bytecodeOffsets) { + super(EXPRENT_ASSIGNMENT); this.left = left; this.right = right; + + addBytecodeOffsets(bytecodeOffsets); } - + @Override public VarType getExprType() { return left.getExprType(); } - + @Override public CheckTypesResult checkExprTypeBounds() { CheckTypesResult result = new CheckTypesResult(); - VarType typeleft = left.getExprType(); - VarType typeright = right.getExprType(); + VarType typeLeft = left.getExprType(); + VarType typeRight = right.getExprType(); - if (typeleft.type_family > typeright.type_family) { - result.addMinTypeExprent(right, VarType.getMinTypeInFamily(typeleft.type_family)); + if (typeLeft.typeFamily > typeRight.typeFamily) { + result.addMinTypeExprent(right, VarType.getMinTypeInFamily(typeLeft.typeFamily)); } - else if (typeleft.type_family < typeright.type_family) { - result.addMinTypeExprent(left, typeright); + else if (typeLeft.typeFamily < typeRight.typeFamily) { + result.addMinTypeExprent(left, typeRight); } else { - result.addMinTypeExprent(left, VarType.getCommonSupertype(typeleft, typeright)); + result.addMinTypeExprent(left, VarType.getCommonSupertype(typeLeft, typeRight)); } return result; } + @Override public List getAllExprents() { - List lst = new ArrayList(); + List lst = new ArrayList<>(); lst.add(left); lst.add(right); return lst; } + @Override public Exprent copy() { - return new AssignmentExprent(left.copy(), right.copy()); + return new AssignmentExprent(left.copy(), right.copy(), bytecode); } + @Override public int getPrecedence() { return 13; } @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { VarType leftType = left.getExprType(); VarType rightType = right.getExprType(); @@ -119,7 +106,7 @@ public class AssignmentExprent extends Exprent { if (field.isStatic() && fd.hasModifier(CodeConstants.ACC_FINAL)) { fieldInClassInit = true; } - if (node.wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()))) { + if (node.getWrapper() != null && node.getWrapper().getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()))) { hiddenField = true; } } @@ -127,10 +114,10 @@ public class AssignmentExprent extends Exprent { } if (hiddenField) { - return ""; + return new TextBuffer(); } - StringBuilder buffer = new StringBuilder(); + TextBuffer buffer = new TextBuffer(); if (fieldInClassInit) { buffer.append(((FieldExprent)left).getName()); @@ -139,43 +126,54 @@ public class AssignmentExprent extends Exprent { buffer.append(left.toJava(indent, tracer)); } - String res = right.toJava(indent, tracer); + if (right.type == EXPRENT_CONST) { + ((ConstExprent) right).adjustConstType(leftType); + } - if (condtype == CONDITION_NONE && + TextBuffer res = right.toJava(indent, tracer); + + if (condType == CONDITION_NONE && !leftType.isSuperset(rightType) && (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT)) { if (right.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) { - res = "(" + res + ")"; + res.enclose("(", ")"); } - res = "(" + ExprProcessor.getCastTypeName(leftType) + ") " + res; + + String re = ExprProcessor.getCastTypeName(leftType); + if ((leftType.type != CodeConstants.TYPE_NULL) && + (!re.equals("Object"))) + { + res.prepend("(" + re + ")"); + } } - buffer.append(condtype == CONDITION_NONE ? " = " : funceq[condtype]).append(res); + buffer.append(condType == CONDITION_NONE ? " = " : OPERATORS[condType]).append(res); tracer.addMapping(bytecode); - return buffer.toString(); + return buffer; } + @Override + public void replaceExprent(Exprent oldExpr, Exprent newExpr) { + if (oldExpr == left) { + left = newExpr; + } + if (oldExpr == right) { + right = newExpr; + } + } + + @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof AssignmentExprent)) return false; + if (!(o instanceof AssignmentExprent)) return false; AssignmentExprent as = (AssignmentExprent)o; return InterpreterUtil.equalObjects(left, as.getLeft()) && InterpreterUtil.equalObjects(right, as.getRight()) && - condtype == as.getCondtype(); - } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if (oldexpr == left) { - left = newexpr; - } - - if (oldexpr == right) { - right = newexpr; - } + condType == as.getCondType(); } // ***************************************************************************** @@ -186,10 +184,6 @@ public class AssignmentExprent extends Exprent { return left; } - public void setLeft(Exprent left) { - this.left = left; - } - public Exprent getRight() { return right; } @@ -198,11 +192,11 @@ public class AssignmentExprent extends Exprent { this.right = right; } - public int getCondtype() { - return condtype; + public int getCondType() { + return condType; } - public void setCondtype(int condtype) { - this.condtype = condtype; + public void setCondType(int condType) { + this.condType = condType; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java index 46e8938..9fa5812 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.exps; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -22,253 +8,234 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.match.MatchEngine; +import org.jetbrains.java.decompiler.struct.match.MatchNode; +import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; +import org.jetbrains.java.decompiler.util.TextUtil; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; +import java.util.*; +import java.util.Map.Entry; public class ConstExprent extends Exprent { - private static final HashMap escapes = new HashMap(); - + private static final Map CHAR_ESCAPES; static { - escapes.put(new Integer(0x8), "\\b"); /* \u0008: backspace BS */ - escapes.put(new Integer(0x9), "\\t"); /* \u0009: horizontal tab HT */ - escapes.put(new Integer(0xA), "\\n"); /* \u000a: linefeed LF */ - escapes.put(new Integer(0xC), "\\f"); /* \u000c: form feed FF */ - escapes.put(new Integer(0xD), "\\r"); /* \u000d: carriage return CR */ - escapes.put(new Integer(0x22), "\\\""); /* \u0022: double quote " */ - escapes.put(new Integer(0x27), "\\\'"); /* \u0027: single quote ' */ - escapes.put(new Integer(0x5C), "\\\\"); /* \u005c: backslash \ */ + CHAR_ESCAPES = new HashMap<>(); + CHAR_ESCAPES.put(0x8, "\\b"); /* \u0008: backspace BS */ + CHAR_ESCAPES.put(0x9, "\\t"); /* \u0009: horizontal tab HT */ + CHAR_ESCAPES.put(0xA, "\\n"); /* \u000a: linefeed LF */ + CHAR_ESCAPES.put(0xC, "\\f"); /* \u000c: form feed FF */ + CHAR_ESCAPES.put(0xD, "\\r"); /* \u000d: carriage return CR */ + //CHAR_ESCAPES.put(0x22, "\\\""); /* \u0022: double quote " */ + CHAR_ESCAPES.put(0x27, "\\'"); /* \u0027: single quote ' */ + CHAR_ESCAPES.put(0x5C, "\\\\"); /* \u005c: backslash \ */ } + private VarType constType; + private final Object value; + private final boolean boolPermitted; - private VarType consttype; - - private Object value; - - private boolean boolPermitted; - - { - this.type = EXPRENT_CONST; + public ConstExprent(int val, boolean boolPermitted, Set bytecodeOffsets) { + this(guessType(val, boolPermitted), val, boolPermitted, bytecodeOffsets); } - public ConstExprent(int val, boolean boolPermitted) { + public ConstExprent(VarType constType, Object value, Set bytecodeOffsets) { + this(constType, value, false, bytecodeOffsets); + } + private ConstExprent(VarType constType, Object value, boolean boolPermitted, Set bytecodeOffsets) { + super(EXPRENT_CONST); + this.constType = constType; + this.value = value; this.boolPermitted = boolPermitted; + addBytecodeOffsets(bytecodeOffsets); + } + + private static VarType guessType(int val, boolean boolPermitted) { if (boolPermitted) { - consttype = VarType.VARTYPE_BOOLEAN; + VarType constType = VarType.VARTYPE_BOOLEAN; if (val != 0 && val != 1) { - consttype = consttype.copy(); - consttype.convinfo |= VarType.FALSEBOOLEAN; + constType = constType.copy(true); } + return constType; + } + else if (0 <= val && val <= 127) { + return VarType.VARTYPE_BYTECHAR; + } + else if (-128 <= val && val <= 127) { + return VarType.VARTYPE_BYTE; + } + else if (0 <= val && val <= 32767) { + return VarType.VARTYPE_SHORTCHAR; + } + else if (-32768 <= val && val <= 32767) { + return VarType.VARTYPE_SHORT; + } + else if (0 <= val && val <= 0xFFFF) { + return VarType.VARTYPE_CHAR; } else { - if (0 <= val && val <= 127) { - consttype = VarType.VARTYPE_BYTECHAR; - } - else if (-128 <= val && val <= 127) { - consttype = VarType.VARTYPE_BYTE; - } - else if (0 <= val && val <= 32767) { - consttype = VarType.VARTYPE_SHORTCHAR; - } - else if (-32768 <= val && val <= 32767) { - consttype = VarType.VARTYPE_SHORT; - } - else if (0 <= val && val <= 0xFFFF) { - consttype = VarType.VARTYPE_CHAR; - } - else { - consttype = VarType.VARTYPE_INT; - } + return VarType.VARTYPE_INT; } - value = new Integer(val); - } - - public ConstExprent(VarType consttype, Object value) { - this.consttype = consttype; - this.value = value; } + @Override public Exprent copy() { - return new ConstExprent(consttype, value); + return new ConstExprent(constType, value, bytecode); } + @Override public VarType getExprType() { - return consttype; + return constType; } + @Override public int getExprentUse() { return Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE; } + @Override public List getAllExprents() { - return new ArrayList(); + return new ArrayList<>(); } @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { boolean literal = DecompilerContext.getOption(IFernflowerPreferences.LITERALS_AS_IS); boolean ascii = DecompilerContext.getOption(IFernflowerPreferences.ASCII_STRING_CHARACTERS); tracer.addMapping(bytecode); - if (consttype.type != CodeConstants.TYPE_NULL && value == null) { - return ExprProcessor.getCastTypeName(consttype); - } - else { - switch (consttype.type) { - case CodeConstants.TYPE_BOOLEAN: - return Boolean.toString(((Integer)value).intValue() != 0); - case CodeConstants.TYPE_CHAR: - Integer val = (Integer)value; - String ret = escapes.get(val); - if (ret == null) { - char c = (char)val.intValue(); - if (c >= 32 && c < 127 || !ascii && InterpreterUtil.isPrintableUnicode(c)) { - ret = String.valueOf(c); - } - else { - ret = InterpreterUtil.charToUnicodeLiteral(c); - } - } - return "\'" + ret + "\'"; - case CodeConstants.TYPE_BYTE: - case CodeConstants.TYPE_BYTECHAR: - case CodeConstants.TYPE_SHORT: - case CodeConstants.TYPE_SHORTCHAR: - case CodeConstants.TYPE_INT: - int ival = ((Integer)value).intValue(); - - String intfield; - if (literal) { - return value.toString(); - } - else if (ival == Integer.MAX_VALUE) { - intfield = "MAX_VALUE"; - } - else if (ival == Integer.MIN_VALUE) { - intfield = "MIN_VALUE"; - } - else { - return value.toString(); - } - return new FieldExprent(intfield, "java/lang/Integer", true, null, FieldDescriptor.INTEGER_DESCRIPTOR).toJava(0, tracer); - case CodeConstants.TYPE_LONG: - long lval = ((Long)value).longValue(); - - String longfield; - if (literal) { - return value.toString() + "L"; - } - else if (lval == Long.MAX_VALUE) { - longfield = "MAX_VALUE"; - } - else if (lval == Long.MIN_VALUE) { - longfield = "MIN_VALUE"; - } - else { - return value.toString() + "L"; - } - return new FieldExprent(longfield, "java/lang/Long", true, null, FieldDescriptor.LONG_DESCRIPTOR).toJava(0, tracer); - case CodeConstants.TYPE_DOUBLE: - double dval = ((Double)value).doubleValue(); - - String doublefield; - if (literal) { - if (Double.isNaN(dval)) { - return "0.0D / 0.0"; - } - else if (dval == Double.POSITIVE_INFINITY) { - return "1.0D / 0.0"; - } - else if (dval == Double.NEGATIVE_INFINITY) { - return "-1.0D / 0.0"; - } - else { - return value.toString() + "D"; - } - } - else if (Double.isNaN(dval)) { - doublefield = "NaN"; - } - else if (dval == Double.POSITIVE_INFINITY) { - doublefield = "POSITIVE_INFINITY"; - } - else if (dval == Double.NEGATIVE_INFINITY) { - doublefield = "NEGATIVE_INFINITY"; - } - else if (dval == Double.MAX_VALUE) { - doublefield = "MAX_VALUE"; - } - else if (dval == Double.MIN_VALUE) { - doublefield = "MIN_VALUE"; - } - else { - return value.toString() + "D"; - } - return new FieldExprent(doublefield, "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR).toJava(0, tracer); - case CodeConstants.TYPE_FLOAT: - float fval = ((Float)value).floatValue(); - - String floatfield; - if (literal) { - if (Double.isNaN(fval)) { - return "0.0F / 0.0"; - } - else if (fval == Double.POSITIVE_INFINITY) { - return "1.0F / 0.0"; - } - else if (fval == Double.NEGATIVE_INFINITY) { - return "-1.0F / 0.0"; - } - else { - return value.toString() + "F"; - } - } - else if (Float.isNaN(fval)) { - floatfield = "NaN"; - } - else if (fval == Float.POSITIVE_INFINITY) { - floatfield = "POSITIVE_INFINITY"; - } - else if (fval == Float.NEGATIVE_INFINITY) { - floatfield = "NEGATIVE_INFINITY"; - } - else if (fval == Float.MAX_VALUE) { - floatfield = "MAX_VALUE"; - } - else if (fval == Float.MIN_VALUE) { - floatfield = "MIN_VALUE"; - } - else { - return value.toString() + "F"; - } - return new FieldExprent(floatfield, "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR).toJava(0, tracer); - case CodeConstants.TYPE_NULL: - return "null"; - case CodeConstants.TYPE_OBJECT: - if (consttype.equals(VarType.VARTYPE_STRING)) { - return "\"" + convertStringToJava(value.toString(), ascii) + "\""; - } - else if (consttype.equals(VarType.VARTYPE_CLASS)) { - String strval = value.toString(); - - VarType classtype; - if (strval.startsWith("[")) { // array of simple type - classtype = new VarType(strval, false); - } - else { // class - classtype = new VarType(strval, true); - } - - return ExprProcessor.getCastTypeName(classtype) + ".class"; - } - } + if (constType.type != CodeConstants.TYPE_NULL && value == null) { + return new TextBuffer(ExprProcessor.getCastTypeName(constType)); } - throw new RuntimeException("invalid constant type"); + switch (constType.type) { + case CodeConstants.TYPE_BOOLEAN: + return new TextBuffer(Boolean.toString((Integer)value != 0)); + + case CodeConstants.TYPE_CHAR: + Integer val = (Integer)value; + String ret = CHAR_ESCAPES.get(val); + if (ret == null) { + char c = (char)val.intValue(); + if (isPrintableAscii(c) || !ascii && TextUtil.isPrintableUnicode(c)) { + ret = String.valueOf(c); + } + else { + ret = TextUtil.charToUnicodeLiteral(c); + } + } + return new TextBuffer(ret).enclose("'", "'"); + + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_BYTECHAR: + case CodeConstants.TYPE_SHORT: + case CodeConstants.TYPE_SHORTCHAR: + case CodeConstants.TYPE_INT: + int intVal = (Integer)value; + if (!literal) { + if (intVal == Integer.MAX_VALUE) { + return new FieldExprent("MAX_VALUE", "java/lang/Integer", true, null, FieldDescriptor.INTEGER_DESCRIPTOR, bytecode).toJava(0, tracer); + } + else if (intVal == Integer.MIN_VALUE) { + return new FieldExprent("MIN_VALUE", "java/lang/Integer", true, null, FieldDescriptor.INTEGER_DESCRIPTOR, bytecode).toJava(0, tracer); + } + } + return new TextBuffer(value.toString()); + + case CodeConstants.TYPE_LONG: + long longVal = (Long)value; + if (!literal) { + if (longVal == Long.MAX_VALUE) { + return new FieldExprent("MAX_VALUE", "java/lang/Long", true, null, FieldDescriptor.LONG_DESCRIPTOR, bytecode).toJava(0, tracer); + } + else if (longVal == Long.MIN_VALUE) { + return new FieldExprent("MIN_VALUE", "java/lang/Long", true, null, FieldDescriptor.LONG_DESCRIPTOR, bytecode).toJava(0, tracer); + } + } + return new TextBuffer(value.toString()).append('L'); + + case CodeConstants.TYPE_FLOAT: + float floatVal = (Float)value; + if (!literal) { + if (Float.isNaN(floatVal)) { + return new FieldExprent("NaN", "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer); + } + else if (floatVal == Float.POSITIVE_INFINITY) { + return new FieldExprent("POSITIVE_INFINITY", "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer); + } + else if (floatVal == Float.NEGATIVE_INFINITY) { + return new FieldExprent("NEGATIVE_INFINITY", "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer); + } + else if (floatVal == Float.MAX_VALUE) { + return new FieldExprent("MAX_VALUE", "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer); + } + else if (floatVal == Float.MIN_VALUE) { + return new FieldExprent("MIN_VALUE", "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer); + } + } + else if (Float.isNaN(floatVal)) { + return new TextBuffer("0.0F / 0.0"); + } + else if (floatVal == Float.POSITIVE_INFINITY) { + return new TextBuffer("1.0F / 0.0"); + } + else if (floatVal == Float.NEGATIVE_INFINITY) { + return new TextBuffer("-1.0F / 0.0"); + } + return new TextBuffer(value.toString()).append('F'); + + case CodeConstants.TYPE_DOUBLE: + double doubleVal = (Double)value; + if (!literal) { + if (Double.isNaN(doubleVal)) { + return new FieldExprent("NaN", "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer); + } + else if (doubleVal == Double.POSITIVE_INFINITY) { + return new FieldExprent("POSITIVE_INFINITY", "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer); + } + else if (doubleVal == Double.NEGATIVE_INFINITY) { + return new FieldExprent("NEGATIVE_INFINITY", "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer); + } + else if (doubleVal == Double.MAX_VALUE) { + return new FieldExprent("MAX_VALUE", "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer); + } + else if (doubleVal == Double.MIN_VALUE) { + return new FieldExprent("MIN_VALUE", "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer); + } + } + else if (Double.isNaN(doubleVal)) { + return new TextBuffer("0.0D / 0.0"); + } + else if (doubleVal == Double.POSITIVE_INFINITY) { + return new TextBuffer("1.0D / 0.0"); + } + else if (doubleVal == Double.NEGATIVE_INFINITY) { + return new TextBuffer("-1.0D / 0.0"); + } + return new TextBuffer(value.toString()).append('D'); + + case CodeConstants.TYPE_NULL: + return new TextBuffer("null"); + + case CodeConstants.TYPE_OBJECT: + if (constType.equals(VarType.VARTYPE_STRING)) { + return new TextBuffer(convertStringToJava(value.toString(), ascii)).enclose("\"", "\""); + } + else if (constType.equals(VarType.VARTYPE_CLASS)) { + String stringVal = value.toString(); + VarType type = new VarType(stringVal, !stringVal.startsWith("[")); + return new TextBuffer(ExprProcessor.getCastTypeName(type)).append(".class"); + } + } + + throw new RuntimeException("invalid constant type: " + constType); + } + + public boolean isNull() { + return CodeConstants.TYPE_NULL == constType.type; } private static String convertStringToJava(String value, boolean ascii) { @@ -298,15 +265,15 @@ public class ConstExprent extends Exprent { case 0x22: //"\\\\\""); // u0022: double quote " buffer.append("\\\""); break; - case 0x27: //"\\\\'"); // u0027: single quote ' - buffer.append("\\\'"); - break; + //case 0x27: //"\\\\'"); // u0027: single quote ' + // buffer.append("\\\'"); + // break; default: - if (c >= 32 && c < 127 || !ascii && InterpreterUtil.isPrintableUnicode(c)) { + if (isPrintableAscii(c) || !ascii && TextUtil.isPrintableUnicode(c)) { buffer.append(c); } else { - buffer.append(InterpreterUtil.charToUnicodeLiteral(c)); + buffer.append(TextUtil.charToUnicodeLiteral(c)); } } } @@ -314,19 +281,25 @@ public class ConstExprent extends Exprent { return buffer.toString(); } - + @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof ConstExprent)) return false; + if (!(o instanceof ConstExprent)) return false; ConstExprent cn = (ConstExprent)o; - return InterpreterUtil.equalObjects(consttype, cn.getConsttype()) && + return InterpreterUtil.equalObjects(constType, cn.getConstType()) && InterpreterUtil.equalObjects(value, cn.getValue()); } - public boolean hasBooleanValue() { + @Override + public int hashCode() { + int result = constType != null ? constType.hashCode() : 0; + result = 31 * result + (value != null ? value.hashCode() : 0); + return result; + } - switch (consttype.type) { + public boolean hasBooleanValue() { + switch (constType.type) { case CodeConstants.TYPE_BOOLEAN: case CodeConstants.TYPE_CHAR: case CodeConstants.TYPE_BYTE: @@ -334,17 +307,15 @@ public class ConstExprent extends Exprent { case CodeConstants.TYPE_SHORT: case CodeConstants.TYPE_SHORTCHAR: case CodeConstants.TYPE_INT: - Integer ival = (Integer)value; - return ival.intValue() == 0 || - (DecompilerContext.getOption(IFernflowerPreferences.BOOLEAN_TRUE_ONE) && ival.intValue() == 1); + int value = (Integer)this.value; + return value == 0 || (DecompilerContext.getOption(IFernflowerPreferences.BOOLEAN_TRUE_ONE) && value == 1); } return false; } public boolean hasValueOne() { - - switch (consttype.type) { + switch (constType.type) { case CodeConstants.TYPE_BOOLEAN: case CodeConstants.TYPE_CHAR: case CodeConstants.TYPE_BYTE: @@ -352,7 +323,7 @@ public class ConstExprent extends Exprent { case CodeConstants.TYPE_SHORT: case CodeConstants.TYPE_SHORTCHAR: case CodeConstants.TYPE_INT: - return ((Integer)value).intValue() == 1; + return (Integer)value == 1; case CodeConstants.TYPE_LONG: return ((Long)value).intValue() == 1; case CodeConstants.TYPE_DOUBLE: @@ -365,42 +336,87 @@ public class ConstExprent extends Exprent { } public static ConstExprent getZeroConstant(int type) { - switch (type) { case CodeConstants.TYPE_INT: - return new ConstExprent(VarType.VARTYPE_INT, new Integer(0)); + return new ConstExprent(VarType.VARTYPE_INT, 0, null); case CodeConstants.TYPE_LONG: - return new ConstExprent(VarType.VARTYPE_LONG, new Long(0)); + return new ConstExprent(VarType.VARTYPE_LONG, 0L, null); case CodeConstants.TYPE_DOUBLE: - return new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(0)); + return new ConstExprent(VarType.VARTYPE_DOUBLE, 0d, null); case CodeConstants.TYPE_FLOAT: - return new ConstExprent(VarType.VARTYPE_FLOAT, new Float(0)); + return new ConstExprent(VarType.VARTYPE_FLOAT, 0f, null); } - throw new RuntimeException("Invalid argument!"); + throw new RuntimeException("Invalid argument: " + type); } - public VarType getConsttype() { - return consttype; + public VarType getConstType() { + return constType; } - public void setConsttype(VarType consttype) { - this.consttype = consttype; + public void setConstType(VarType constType) { + this.constType = constType; } + public void adjustConstType(VarType expectedType) { + // BYTECHAR and SHORTCHAR => CHAR in the CHAR context + if ((expectedType.equals(VarType.VARTYPE_CHAR) || expectedType.equals(VarType.VARTYPE_CHARACTER)) && + (constType.equals(VarType.VARTYPE_BYTECHAR) || constType.equals(VarType.VARTYPE_SHORTCHAR))) { + int intValue = getIntValue(); + if (isPrintableAscii(intValue) || CHAR_ESCAPES.containsKey(intValue)) { + setConstType(VarType.VARTYPE_CHAR); + } + } + // BYTE, BYTECHAR, SHORTCHAR, SHORT, CHAR => INT in the INT context + else if ((expectedType.equals(VarType.VARTYPE_INT) || expectedType.equals(VarType.VARTYPE_INTEGER)) && + constType.typeFamily == CodeConstants.TYPE_FAMILY_INTEGER) { + setConstType(VarType.VARTYPE_INT); + } + } + + private static boolean isPrintableAscii(int c) { + return c >= 32 && c < 127; + } + + public Object getValue() { return value; } public int getIntValue() { - return ((Integer)value).intValue(); + return (Integer)value; } public boolean isBoolPermitted() { return boolPermitted; } - public void setBoolPermitted(boolean boolPermitted) { - this.boolPermitted = boolPermitted; + // ***************************************************************************** + // IMatchable implementation + // ***************************************************************************** + + @Override + public boolean match(MatchNode matchNode, MatchEngine engine) { + if (!super.match(matchNode, engine)) { + return false; + } + + for (Entry rule : matchNode.getRules().entrySet()) { + RuleValue value = rule.getValue(); + MatchProperties key = rule.getKey(); + + if (key == MatchProperties.EXPRENT_CONSTTYPE) { + if (!value.value.equals(this.constType)) { + return false; + } + } + else if (key == MatchProperties.EXPRENT_CONSTVALUE) { + if (value.isVariable() && !engine.checkAndSetVariableValue(value.value.toString(), this.value)) { + return false; + } + } + } + + return true; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java index e54b365..b3cc07d 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java @@ -1,75 +1,63 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.exps; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.struct.attr.StructExceptionsAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.match.MatchEngine; +import org.jetbrains.java.decompiler.struct.match.MatchNode; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.ArrayList; import java.util.List; - +import java.util.Set; public class ExitExprent extends Exprent { public static final int EXIT_RETURN = 0; public static final int EXIT_THROW = 1; - // return or throw statement - private int exittype; - + private final int exitType; private Exprent value; + private final VarType retType; - private VarType rettype; - - { - this.type = EXPRENT_EXIT; - } - - public ExitExprent(int exittype, Exprent value, VarType rettype) { - this.exittype = exittype; + public ExitExprent(int exitType, Exprent value, VarType retType, Set bytecodeOffsets) { + super(EXPRENT_EXIT); + this.exitType = exitType; this.value = value; - this.rettype = rettype; + this.retType = retType; + + addBytecodeOffsets(bytecodeOffsets); } + @Override public Exprent copy() { - return new ExitExprent(exittype, value == null ? null : value.copy(), rettype); + return new ExitExprent(exitType, value == null ? null : value.copy(), retType, bytecode); } + @Override public CheckTypesResult checkExprTypeBounds() { CheckTypesResult result = new CheckTypesResult(); - if (exittype == EXIT_RETURN && rettype.type != CodeConstants.TYPE_VOID) { - result.addMinTypeExprent(value, VarType.getMinTypeInFamily(rettype.type_family)); - result.addMaxTypeExprent(value, rettype); + if (exitType == EXIT_RETURN && retType.type != CodeConstants.TYPE_VOID) { + result.addMinTypeExprent(value, VarType.getMinTypeInFamily(retType.typeFamily)); + result.addMaxTypeExprent(value, retType); } return result; } + @Override public List getAllExprents() { - List lst = new ArrayList(); + List lst = new ArrayList<>(); if (value != null) { lst.add(value); } @@ -77,81 +65,93 @@ public class ExitExprent extends Exprent { } @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { - + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { tracer.addMapping(bytecode); - if (exittype == EXIT_RETURN) { - TextBuffer buffer = new TextBuffer(); + if (exitType == EXIT_RETURN) { + TextBuffer buffer = new TextBuffer("return"); - if (rettype.type != CodeConstants.TYPE_VOID) { - buffer.append(" "); - ExprProcessor.getCastedExprent(value, rettype, buffer, indent, false, tracer); + if (retType.type != CodeConstants.TYPE_VOID) { + buffer.append(' '); + ExprProcessor.getCastedExprent(value, retType, buffer, indent, false, tracer); } - return "return" + buffer.toString(); + return buffer; } else { - - MethodWrapper meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + MethodWrapper method = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); ClassNode node = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE)); - if (meth != null && node != null) { - StructExceptionsAttribute attr = (StructExceptionsAttribute)meth.methodStruct.getAttributes().getWithKey("Exceptions"); + if (method != null && node != null) { + StructExceptionsAttribute attr = method.methodStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_EXCEPTIONS); if (attr != null) { String classname = null; for (int i = 0; i < attr.getThrowsExceptions().size(); i++) { - String excclassname = attr.getExcClassname(i, node.classStruct.getPool()); - if ("java/lang/Throwable".equals(excclassname)) { - classname = excclassname; + String exClassName = attr.getExcClassname(i, node.classStruct.getPool()); + if ("java/lang/Throwable".equals(exClassName)) { + classname = exClassName; break; } - else if ("java/lang/Exception".equals(excclassname)) { - classname = excclassname; + else if ("java/lang/Exception".equals(exClassName)) { + classname = exClassName; } } if (classname != null) { - VarType exctype = new VarType(classname, true); - - TextBuffer buffer = new TextBuffer(); - ExprProcessor.getCastedExprent(value, exctype, buffer, indent, false, tracer); - - return "throw " + buffer.toString(); + VarType exType = new VarType(classname, true); + TextBuffer buffer = new TextBuffer("throw "); + ExprProcessor.getCastedExprent(value, exType, buffer, indent, false, tracer); + return buffer; } } } - return "throw " + value.toJava(indent, tracer); + return value.toJava(indent, tracer).prepend("throw "); } } + @Override + public void replaceExprent(Exprent oldExpr, Exprent newExpr) { + if (oldExpr == value) { + value = newExpr; + } + } + + @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof ExitExprent)) return false; + if (!(o instanceof ExitExprent)) return false; ExitExprent et = (ExitExprent)o; - return exittype == et.getExittype() && + return exitType == et.getExitType() && InterpreterUtil.equalObjects(value, et.getValue()); } - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if (oldexpr == value) { - value = newexpr; - } - } - - public int getExittype() { - return exittype; + public int getExitType() { + return exitType; } public Exprent getValue() { return value; } - public VarType getRettype() { - return rettype; + public VarType getRetType() { + return retType; } -} + + // ***************************************************************************** + // IMatchable implementation + // ***************************************************************************** + + @Override + public boolean match(MatchNode matchNode, MatchEngine engine) { + if (!super.match(matchNode, engine)) { + return false; + } + + Integer type = (Integer)matchNode.getRuleValue(MatchProperties.EXPRENT_EXITTYPE); + return type == null || this.exitType == type; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java new file mode 100644 index 0000000..58a653a --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java @@ -0,0 +1,45 @@ +// 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; +import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.main.rels.ClassWrapper; +import org.jetbrains.java.decompiler.main.rels.MethodWrapper; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +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; + } + + public static List getSyntheticParametersMask(ClassNode node, String descriptor, int parameters) { + List mask = null; + + ClassWrapper wrapper = node.getWrapper(); + if (wrapper != null) { + // own class + MethodWrapper methodWrapper = wrapper.getMethodWrapper(CodeConstants.INIT_NAME, descriptor); + if (methodWrapper == null) { + if (DecompilerContext.getOption(IFernflowerPreferences.IGNORE_INVALID_BYTECODE)) { + return null; + } + throw new RuntimeException("Constructor " + node.classStruct.qualifiedName + "." + CodeConstants.INIT_NAME + descriptor + " not found"); + } + mask = methodWrapper.synthParameters; + } + else if (parameters > 0 && node.type == ClassNode.CLASS_MEMBER && (node.access & CodeConstants.ACC_STATIC) == 0) { + // non-static member class + mask = new ArrayList<>(Collections.nCopies(parameters, null)); + mask.set(0, new VarVersionPair(-1, 0)); + } + + return mask; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java index 5c8d6be..bb303e8 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java @@ -1,40 +1,31 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ package org.jetbrains.java.decompiler.modules.decompiler.exps; import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.util.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.match.IMatchable; +import org.jetbrains.java.decompiler.struct.match.MatchEngine; +import org.jetbrains.java.decompiler.struct.match.MatchNode; +import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue; -import java.util.ArrayList; +import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Map.Entry; import java.util.Set; - -public class Exprent { - +public class Exprent implements IMatchable { public static final int MULTIPLE_USES = 1; public static final int SIDE_EFFECTS_FREE = 2; public static final int BOTH_FLAGS = 3; - public static final int EXPRENT_ARRAY = 1; public static final int EXPRENT_ASSIGNMENT = 2; public static final int EXPRENT_CONST = 3; @@ -50,16 +41,13 @@ public class Exprent { public static final int EXPRENT_ANNOTATION = 13; public static final int EXPRENT_ASSERT = 14; - public int type; + public final int type; + public final int id; + public Set bytecode = null; // offsets of bytecode instructions decompiled to this exprent - public int id; - - //offsets of bytecode instructions decompiled to this exprent - public Set bytecode = new HashSet(); - - { - // set exprent id - id = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.EXPRENT_COUNTER); + public Exprent(int type) { + this.type = type; + this.id = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.EXPRESSION_COUNTER); } public int getPrecedence() { @@ -75,48 +63,42 @@ public class Exprent { } public CheckTypesResult checkExprTypeBounds() { - return new CheckTypesResult(); + return null; } public boolean containsExprent(Exprent exprent) { - - List listTemp = new ArrayList(getAllExprents(true)); - listTemp.add(this); - - for (Exprent lstexpr : listTemp) { - if (lstexpr.equals(exprent)) { + if (equals(exprent)) { + return true; + } + List lst = getAllExprents(); + for (int i = lst.size() - 1; i >= 0; i--) { + if (lst.get(i).containsExprent(exprent)) { return true; } } - return false; } public List getAllExprents(boolean recursive) { List lst = getAllExprents(); - if (recursive) { for (int i = lst.size() - 1; i >= 0; i--) { lst.addAll(lst.get(i).getAllExprents(true)); } } - return lst; } - public Set getAllVariables() { - - HashSet set = new HashSet(); - + public Set getAllVariables() { List lstAllExprents = getAllExprents(true); lstAllExprents.add(this); + Set set = new HashSet<>(); for (Exprent expr : lstAllExprents) { if (expr.type == EXPRENT_VAR) { - set.add(new VarVersionPaar((VarExprent)expr)); + set.add(new VarVersionPair((VarExprent)expr)); } } - return set; } @@ -128,10 +110,72 @@ public class Exprent { throw new RuntimeException("not implemented"); } - public String toJava(int indent, BytecodeMappingTracer tracer) { + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { throw new RuntimeException("not implemented"); } - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + public void replaceExprent(Exprent oldExpr, Exprent newExpr) { } + + public void addBytecodeOffsets(Collection bytecodeOffsets) { + if (bytecodeOffsets != null && !bytecodeOffsets.isEmpty()) { + if (bytecode == null) { + bytecode = new HashSet<>(bytecodeOffsets); + } + else { + bytecode.addAll(bytecodeOffsets); + } + } } -} + + // ***************************************************************************** + // IMatchable implementation + // ***************************************************************************** + + @Override + public IMatchable findObject(MatchNode matchNode, int index) { + if (matchNode.getType() != MatchNode.MATCHNODE_EXPRENT) { + return null; + } + + List lstAllExprents = getAllExprents(); + if (lstAllExprents == null || lstAllExprents.isEmpty()) { + return null; + } + + String position = (String)matchNode.getRuleValue(MatchProperties.EXPRENT_POSITION); + if (position != null) { + if (position.matches("-?\\d+")) { + return lstAllExprents.get((lstAllExprents.size() + Integer.parseInt(position)) % lstAllExprents.size()); // care for negative positions + } + } + else if (index < lstAllExprents.size()) { // use 'index' parameter + return lstAllExprents.get(index); + } + + return null; + } + + @Override + public boolean match(MatchNode matchNode, MatchEngine engine) { + if (matchNode.getType() != MatchNode.MATCHNODE_EXPRENT) { + return false; + } + + for (Entry rule : matchNode.getRules().entrySet()) { + MatchProperties key = rule.getKey(); + if (key == MatchProperties.EXPRENT_TYPE && this.type != (Integer)rule.getValue().value) { + return false; + } + if (key == MatchProperties.EXPRENT_RET && !engine.checkAndSetVariableValue((String)rule.getValue().value, this)) { + return false; + } + } + + return true; + } + + @Override + public String toString() { + return toJava(0, BytecodeMappingTracer.DUMMY).toString(); + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java index 5363477..642a86b 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java @@ -1,124 +1,110 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ package org.jetbrains.java.decompiler.modules.decompiler.exps; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; -import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; +import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute; import org.jetbrains.java.decompiler.struct.consts.LinkConstant; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.match.MatchEngine; +import org.jetbrains.java.decompiler.struct.match.MatchNode; +import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; +import org.jetbrains.java.decompiler.util.TextUtil; import java.util.ArrayList; import java.util.List; - +import java.util.Set; public class FieldExprent extends Exprent { - - private String name; - - private String classname; - - private boolean isStatic; - + private final String name; + private final String classname; + private final boolean isStatic; private Exprent instance; + private final FieldDescriptor descriptor; - private FieldDescriptor descriptor; - - { - this.type = EXPRENT_FIELD; + public FieldExprent(LinkConstant cn, Exprent instance, Set bytecodeOffsets) { + this(cn.elementname, cn.classname, instance == null, instance, FieldDescriptor.parseDescriptor(cn.descriptor), bytecodeOffsets); } - public FieldExprent(LinkConstant cn, Exprent instance) { - - this.instance = instance; - - if (instance == null) { - isStatic = true; - } - - classname = cn.classname; - name = cn.elementname; - descriptor = FieldDescriptor.parseDescriptor(cn.descriptor); - } - - public FieldExprent(String name, String classname, boolean isStatic, Exprent instance, FieldDescriptor descriptor) { + public FieldExprent(String name, String classname, boolean isStatic, Exprent instance, FieldDescriptor descriptor, Set bytecodeOffsets) { + super(EXPRENT_FIELD); this.name = name; this.classname = classname; this.isStatic = isStatic; this.instance = instance; this.descriptor = descriptor; + + addBytecodeOffsets(bytecodeOffsets); } + @Override public VarType getExprType() { return descriptor.type; } + @Override public int getExprentUse() { - if (instance == null) { - return Exprent.MULTIPLE_USES; - } - else { - return instance.getExprentUse() & Exprent.MULTIPLE_USES; - } + return 0; // multiple references to a field considered dangerous in a multithreaded environment, thus no Exprent.MULTIPLE_USES set here } + @Override public List getAllExprents() { - List lst = new ArrayList(); + List lst = new ArrayList<>(); if (instance != null) { lst.add(instance); } return lst; } + @Override public Exprent copy() { - return new FieldExprent(name, classname, isStatic, instance == null ? null : instance.copy(), descriptor); + return new FieldExprent(name, classname, isStatic, instance == null ? null : instance.copy(), descriptor, bytecode); + } + + private boolean isAmbiguous() { + MethodWrapper method = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + if (method != null) { + StructLocalVariableTableAttribute attr = method.methodStruct.getLocalVariableAttr(); + if (attr != null) { + return attr.containsName(name); + } + } + + return false; } @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { - StringBuilder buf = new StringBuilder(); - + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + TextBuffer buf = new TextBuffer(); if (isStatic) { ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); - if (true || node == null || !classname.equals(node.classStruct.qualifiedName)) { // Spigot - buf.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(classname))); + if (node == null || !classname.equals(node.classStruct.qualifiedName) || isAmbiguous()) { + buf.append(DecompilerContext.getImportCollector().getShortNameInClassContext(ExprProcessor.buildJavaClassName(classname))); buf.append("."); } } else { - String super_qualifier = null; if (instance != null && instance.type == Exprent.EXPRENT_VAR) { - VarExprent instvar = (VarExprent)instance; - VarVersionPaar varpaar = new VarVersionPaar(instvar); + VarExprent instVar = (VarExprent)instance; + VarVersionPair pair = new VarVersionPair(instVar); - MethodWrapper current_meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + MethodWrapper currentMethod = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); - if (current_meth != null) { // FIXME: remove - String this_classname = current_meth.varproc.getThisvars().get(varpaar); + if (currentMethod != null) { // FIXME: remove + String this_classname = currentMethod.varproc.getThisVars().get(pair); if (this_classname != null) { if (!classname.equals(this_classname)) { // TODO: direct comparison to the super class? @@ -129,13 +115,7 @@ public class FieldExprent extends Exprent { } if (super_qualifier != null) { - StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE)).classStruct; - - if (!super_qualifier.equals(current_class.qualifiedName)) { - buf.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(super_qualifier))); - buf.append("."); - } - buf.append("super"); + TextUtil.writeQualifiedSuper(buf, super_qualifier); } else { TextBuffer buff = new TextBuffer(); @@ -162,12 +142,20 @@ public class FieldExprent extends Exprent { tracer.addMapping(bytecode); - return buf.toString(); + return buf; } + @Override + public void replaceExprent(Exprent oldExpr, Exprent newExpr) { + if (oldExpr == instance) { + instance = newExpr; + } + } + + @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof FieldExprent)) return false; + if (!(o instanceof FieldExprent)) return false; FieldExprent ft = (FieldExprent)o; return InterpreterUtil.equalObjects(name, ft.getName()) && @@ -177,12 +165,6 @@ public class FieldExprent extends Exprent { InterpreterUtil.equalObjects(descriptor, ft.getDescriptor()); } - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if (oldexpr == instance) { - instance = newexpr; - } - } - public String getClassname() { return classname; } @@ -202,4 +184,27 @@ public class FieldExprent extends Exprent { public String getName() { return name; } -} + + // ***************************************************************************** + // IMatchable implementation + // ***************************************************************************** + + @Override + public boolean match(MatchNode matchNode, MatchEngine engine) { + if (!super.match(matchNode, engine)) { + return false; + } + + RuleValue rule = matchNode.getRules().get(MatchProperties.EXPRENT_FIELD_NAME); + if (rule != null) { + if (rule.isVariable()) { + return engine.checkAndSetVariableValue((String)rule.value, this.name); + } + else { + return rule.value.equals(this.name); + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java index 52f09c2..6bdd8db 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java @@ -1,17 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ package org.jetbrains.java.decompiler.modules.decompiler.exps; @@ -20,14 +8,13 @@ import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.match.MatchEngine; +import org.jetbrains.java.decompiler.struct.match.MatchNode; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.ListStack; +import org.jetbrains.java.decompiler.util.TextBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; - +import java.util.*; public class FunctionExprent extends Exprent { @@ -46,8 +33,8 @@ public class FunctionExprent extends Exprent { public static final int FUNCTION_SHR = 9; public static final int FUNCTION_USHR = 10; - public static final int FUNCTION_BITNOT = 11; - public static final int FUNCTION_BOOLNOT = 12; + public static final int FUNCTION_BIT_NOT = 11; + public static final int FUNCTION_BOOL_NOT = 12; public static final int FUNCTION_NEG = 13; public final static int FUNCTION_I2L = 14; @@ -69,7 +56,7 @@ public class FunctionExprent extends Exprent { public final static int FUNCTION_CAST = 29; public final static int FUNCTION_INSTANCEOF = 30; - public final static int FUNCTION_ARRAYLENGTH = 31; + public final static int FUNCTION_ARRAY_LENGTH = 31; public final static int FUNCTION_IMM = 32; public final static int FUNCTION_MMI = 33; @@ -95,9 +82,9 @@ public class FunctionExprent extends Exprent { public static final int FUNCTION_CADD = 48; public static final int FUNCTION_COR = 49; - public static final int FUNCTION_STRCONCAT = 50; + public static final int FUNCTION_STR_CONCAT = 50; - private static final VarType[] types = new VarType[]{ + private static final VarType[] TYPES = { VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, @@ -115,7 +102,7 @@ public class FunctionExprent extends Exprent { VarType.VARTYPE_SHORT }; - private static final String[] operators = new String[]{ + private static final String[] OPERATORS = { " + ", " - ", " * ", @@ -138,81 +125,74 @@ public class FunctionExprent extends Exprent { " + " }; - private static final int[] precedence = new int[]{ - 3, // FUNCTION_ADD - 3, // FUNCTION_SUB - 2, // FUNCTION_MUL - 2, // FUNCTION_DIV - 7, // FUNCTION_AND - 9, // FUNCTION_OR - 8, // FUNCTION_XOR - 2, // FUNCTION_REM - 4, // FUNCTION_SHL - 4, // FUNCTION_SHR - 4, // FUNCTION_USHR - 1, // FUNCTION_BITNOT - 1, // FUNCTION_BOOLNOT - 1, // FUNCTION_NEG - 1, // FUNCTION_I2L - 1, // FUNCTION_I2F - 1, // FUNCTION_I2D - 1, // FUNCTION_L2I - 1, // FUNCTION_L2F - 1, // FUNCTION_L2D - 1, // FUNCTION_F2I - 1, // FUNCTION_F2L - 1, // FUNCTION_F2D - 1, // FUNCTION_D2I - 1, // FUNCTION_D2L - 1, // FUNCTION_D2F - 1, // FUNCTION_I2B - 1, // FUNCTION_I2C - 1, // FUNCTION_I2S - 1, // FUNCTION_CAST - 6, // FUNCTION_INSTANCEOF - 0, // FUNCTION_ARRAYLENGTH - 1, // FUNCTION_IMM - 1, // FUNCTION_MMI - 1, // FUNCTION_IPP - 1, // FUNCTION_PPI - 12, // FUNCTION_IFF - -1, // FUNCTION_LCMP - -1, // FUNCTION_FCMPL - -1, // FUNCTION_FCMPG - -1, // FUNCTION_DCMPL - -1, // FUNCTION_DCMPG - 6, // FUNCTION_EQ = 41; - 6, // FUNCTION_NE = 42; - 5, // FUNCTION_LT = 43; - 5, // FUNCTION_GE = 44; - 5, // FUNCTION_GT = 45; - 5, // FUNCTION_LE = 46; - 10, // FUNCTION_CADD = 47; - 11, // FUNCTION_COR = 48; - 3 // FUNCTION_STRCONCAT = 49; + private static final int[] PRECEDENCE = { + 3, // FUNCTION_ADD + 3, // FUNCTION_SUB + 2, // FUNCTION_MUL + 2, // FUNCTION_DIV + 7, // FUNCTION_AND + 9, // FUNCTION_OR + 8, // FUNCTION_XOR + 2, // FUNCTION_REM + 4, // FUNCTION_SHL + 4, // FUNCTION_SHR + 4, // FUNCTION_USHR + 1, // FUNCTION_BIT_NOT + 1, // FUNCTION_BOOL_NOT + 1, // FUNCTION_NEG + 1, // FUNCTION_I2L + 1, // FUNCTION_I2F + 1, // FUNCTION_I2D + 1, // FUNCTION_L2I + 1, // FUNCTION_L2F + 1, // FUNCTION_L2D + 1, // FUNCTION_F2I + 1, // FUNCTION_F2L + 1, // FUNCTION_F2D + 1, // FUNCTION_D2I + 1, // FUNCTION_D2L + 1, // FUNCTION_D2F + 1, // FUNCTION_I2B + 1, // FUNCTION_I2C + 1, // FUNCTION_I2S + 1, // FUNCTION_CAST + 6, // FUNCTION_INSTANCEOF + 0, // FUNCTION_ARRAY_LENGTH + 1, // FUNCTION_IMM + 1, // FUNCTION_MMI + 1, // FUNCTION_IPP + 1, // FUNCTION_PPI + 12, // FUNCTION_IFF + -1, // FUNCTION_LCMP + -1, // FUNCTION_FCMPL + -1, // FUNCTION_FCMPG + -1, // FUNCTION_DCMPL + -1, // FUNCTION_DCMPG + 6, // FUNCTION_EQ = 41; + 6, // FUNCTION_NE = 42; + 5, // FUNCTION_LT = 43; + 5, // FUNCTION_GE = 44; + 5, // FUNCTION_GT = 45; + 5, // FUNCTION_LE = 46; + 10, // FUNCTION_CADD = 47; + 11, // FUNCTION_COR = 48; + 3 // FUNCTION_STR_CONCAT = 49; }; - private static final HashSet associativity = - new HashSet(Arrays.asList(new Integer[]{FUNCTION_ADD, FUNCTION_MUL, FUNCTION_AND, - FUNCTION_OR, FUNCTION_XOR, FUNCTION_CADD, FUNCTION_COR, FUNCTION_STRCONCAT})); - - private int functype; + private static final Set ASSOCIATIVITY = new HashSet<>(Arrays.asList( + FUNCTION_ADD, FUNCTION_MUL, FUNCTION_AND, FUNCTION_OR, FUNCTION_XOR, FUNCTION_CADD, FUNCTION_COR, FUNCTION_STR_CONCAT)); + private int funcType; private VarType implicitType; + private final List lstOperands; - private List lstOperands = new ArrayList(); + public FunctionExprent(int funcType, ListStack stack, Set bytecodeOffsets) { + this(funcType, new ArrayList<>(), bytecodeOffsets); - { - this.type = EXPRENT_FUNCTION; - } - - public FunctionExprent(int functype, ListStack stack) { - this.functype = functype; - if (functype >= FUNCTION_BITNOT && functype <= FUNCTION_PPI && functype != FUNCTION_CAST - && functype != FUNCTION_INSTANCEOF) { + if (funcType >= FUNCTION_BIT_NOT && funcType <= FUNCTION_PPI && funcType != FUNCTION_CAST && funcType != FUNCTION_INSTANCEOF) { lstOperands.add(stack.pop()); } - else if (functype == FUNCTION_IIF) { + else if (funcType == FUNCTION_IIF) { throw new RuntimeException("no direct instantiation possible"); } else { @@ -222,37 +202,44 @@ public class FunctionExprent extends Exprent { } } - public FunctionExprent(int functype, List operands) { - this.functype = functype; + public FunctionExprent(int funcType, List operands, Set bytecodeOffsets) { + super(EXPRENT_FUNCTION); + this.funcType = funcType; this.lstOperands = operands; + + addBytecodeOffsets(bytecodeOffsets); } + public FunctionExprent(int funcType, Exprent operand, Set bytecodeOffsets) { + this(funcType, new ArrayList<>(1), bytecodeOffsets); + lstOperands.add(operand); + } + + @Override public VarType getExprType() { VarType exprType = null; - if (functype <= FUNCTION_NEG || functype == FUNCTION_IPP || functype == FUNCTION_PPI - || functype == FUNCTION_IMM || functype == FUNCTION_MMI) { - + if (funcType <= FUNCTION_NEG || funcType == FUNCTION_IPP || funcType == FUNCTION_PPI || funcType == FUNCTION_IMM || funcType == FUNCTION_MMI) { VarType type1 = lstOperands.get(0).getExprType(); VarType type2 = null; if (lstOperands.size() > 1) { type2 = lstOperands.get(1).getExprType(); } - switch (functype) { + switch (funcType) { case FUNCTION_IMM: case FUNCTION_MMI: case FUNCTION_IPP: case FUNCTION_PPI: exprType = implicitType; break; - case FUNCTION_BOOLNOT: + case FUNCTION_BOOL_NOT: exprType = VarType.VARTYPE_BOOLEAN; break; case FUNCTION_SHL: case FUNCTION_SHR: case FUNCTION_USHR: - case FUNCTION_BITNOT: + case FUNCTION_BIT_NOT: case FUNCTION_NEG: exprType = getMaxVarType(new VarType[]{type1}); break; @@ -274,14 +261,13 @@ public class FunctionExprent extends Exprent { } } } - else if (functype == FUNCTION_CAST) { + else if (funcType == FUNCTION_CAST) { exprType = lstOperands.get(1).getExprType(); } - else if (functype == FUNCTION_IIF) { + else if (funcType == FUNCTION_IIF) { Exprent param1 = lstOperands.get(1); Exprent param2 = lstOperands.get(2); VarType supertype = VarType.getCommonSupertype(param1.getExprType(), param2.getExprType()); - if (param1.type == Exprent.EXPRENT_CONST && param2.type == Exprent.EXPRENT_CONST && supertype.type != CodeConstants.TYPE_BOOLEAN && VarType.VARTYPE_INT.isSuperset(supertype)) { exprType = VarType.VARTYPE_INT; @@ -290,25 +276,25 @@ public class FunctionExprent extends Exprent { exprType = supertype; } } - else if (functype == FUNCTION_STRCONCAT) { + else if (funcType == FUNCTION_STR_CONCAT) { exprType = VarType.VARTYPE_STRING; } - else if (functype >= FUNCTION_EQ || functype == FUNCTION_INSTANCEOF) { + else if (funcType >= FUNCTION_EQ || funcType == FUNCTION_INSTANCEOF) { exprType = VarType.VARTYPE_BOOLEAN; } - else if (functype >= FUNCTION_ARRAYLENGTH) { + else if (funcType >= FUNCTION_ARRAY_LENGTH) { exprType = VarType.VARTYPE_INT; } else { - exprType = types[functype - FUNCTION_I2L]; + exprType = TYPES[funcType - FUNCTION_I2L]; } return exprType; } + @Override public int getExprentUse() { - - if (functype >= FUNCTION_IMM && functype <= FUNCTION_PPI) { + if (funcType >= FUNCTION_IMM && funcType <= FUNCTION_PPI) { return 0; } else { @@ -320,6 +306,7 @@ public class FunctionExprent extends Exprent { } } + @Override public CheckTypesResult checkExprTypeBounds() { CheckTypesResult result = new CheckTypesResult(); @@ -333,15 +320,12 @@ public class FunctionExprent extends Exprent { type2 = param2.getExprType(); } - switch (functype) { + switch (funcType) { case FUNCTION_IIF: VarType supertype = getExprType(); - if (supertype == null) { - supertype = getExprType(); - } result.addMinTypeExprent(param1, VarType.VARTYPE_BOOLEAN); - result.addMinTypeExprent(param2, VarType.getMinTypeInFamily(supertype.type_family)); - result.addMinTypeExprent(lstOperands.get(2), VarType.getMinTypeInFamily(supertype.type_family)); + result.addMinTypeExprent(param2, VarType.getMinTypeInFamily(supertype.typeFamily)); + result.addMinTypeExprent(lstOperands.get(2), VarType.getMinTypeInFamily(supertype.typeFamily)); break; case FUNCTION_I2L: case FUNCTION_I2F: @@ -372,8 +356,8 @@ public class FunctionExprent extends Exprent { case FUNCTION_GT: case FUNCTION_LE: result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); - case FUNCTION_BITNOT: - // case FUNCTION_BOOLNOT: + case FUNCTION_BIT_NOT: + // case FUNCTION_BOOL_NOT: case FUNCTION_NEG: result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); break; @@ -383,13 +367,10 @@ public class FunctionExprent extends Exprent { case FUNCTION_EQ: case FUNCTION_NE: { if (type1.type == CodeConstants.TYPE_BOOLEAN) { - if (type2.isStrictSuperset(type1)) { - result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); } else { // both are booleans - boolean param1_false_boolean = type1.isFalseBoolean() || (param1.type == Exprent.EXPRENT_CONST && !((ConstExprent)param1).hasBooleanValue()); boolean param2_false_boolean = @@ -402,7 +383,6 @@ public class FunctionExprent extends Exprent { } } else if (type2.type == CodeConstants.TYPE_BOOLEAN) { - if (type1.isStrictSuperset(type2)) { result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); } @@ -413,142 +393,155 @@ public class FunctionExprent extends Exprent { return result; } + @Override public List getAllExprents() { - List lst = new ArrayList(); - lst.addAll(lstOperands); - return lst; + return new ArrayList<>(lstOperands); } + @Override public Exprent copy() { - List lst = new ArrayList(); + List lst = new ArrayList<>(); for (Exprent expr : lstOperands) { lst.add(expr.copy()); } - FunctionExprent func = new FunctionExprent(functype, lst); + FunctionExprent func = new FunctionExprent(funcType, lst, bytecode); func.setImplicitType(implicitType); return func; } + @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof FunctionExprent)) return false; + if (!(o instanceof FunctionExprent)) return false; FunctionExprent fe = (FunctionExprent)o; - return functype == fe.getFunctype() && + return funcType == fe.getFuncType() && InterpreterUtil.equalLists(lstOperands, fe.getLstOperands()); // TODO: order of operands insignificant } - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + @Override + public void replaceExprent(Exprent oldExpr, Exprent newExpr) { for (int i = 0; i < lstOperands.size(); i++) { - if (oldexpr == lstOperands.get(i)) { - lstOperands.set(i, newexpr); + if (oldExpr == lstOperands.get(i)) { + lstOperands.set(i, newExpr); } } } @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { - + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { tracer.addMapping(bytecode); - if (functype <= FUNCTION_USHR) { - return wrapOperandString(lstOperands.get(0), false, indent, tracer) + operators[functype] + - wrapOperandString(lstOperands.get(1), true, indent, tracer); + if (funcType <= FUNCTION_USHR) { + return wrapOperandString(lstOperands.get(0), false, indent, tracer) + .append(OPERATORS[funcType]) + .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)); } - if (functype >= FUNCTION_EQ) { - return wrapOperandString(lstOperands.get(0), false, indent, tracer) + operators[functype - FUNCTION_EQ + 11] + - wrapOperandString(lstOperands.get(1), true, indent, tracer); + // try to determine more accurate type for 'char' literals + if (funcType >= FUNCTION_EQ) { + if (funcType <= FUNCTION_LE) { + Exprent left = lstOperands.get(0); + Exprent right = lstOperands.get(1); + + if (right.type == EXPRENT_CONST) { + ((ConstExprent) right).adjustConstType(left.getExprType()); + } + else if (left.type == EXPRENT_CONST) { + ((ConstExprent) left).adjustConstType(right.getExprType()); + } + } + + return wrapOperandString(lstOperands.get(0), false, indent, tracer) + .append(OPERATORS[funcType - FUNCTION_EQ + 11]) + .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)); } - switch (functype) { - case FUNCTION_BITNOT: - return "~" + wrapOperandString(lstOperands.get(0), true, indent, tracer); - case FUNCTION_BOOLNOT: - return "!" + wrapOperandString(lstOperands.get(0), true, indent, tracer); + switch (funcType) { + case FUNCTION_BIT_NOT: + return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("~"); + case FUNCTION_BOOL_NOT: + return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("!"); case FUNCTION_NEG: - return "-" + wrapOperandString(lstOperands.get(0), true, indent, tracer); + return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("-"); case FUNCTION_CAST: - return "(" + lstOperands.get(1).toJava(indent, tracer) + ") " + wrapOperandString(lstOperands.get(0), true, indent, tracer); - case FUNCTION_ARRAYLENGTH: + return lstOperands.get(1).toJava(indent, tracer).enclose("(", ")").append(wrapOperandString(lstOperands.get(0), true, indent, tracer)); + case FUNCTION_ARRAY_LENGTH: Exprent arr = lstOperands.get(0); - String res = wrapOperandString(arr, false, indent, tracer); - if (arr.getExprType().arraydim == 0) { - VarType objarr = VarType.VARTYPE_OBJECT.copy(); - objarr.arraydim = 1; // type family does not change - - res = "((" + ExprProcessor.getCastTypeName(objarr) + ")" + res + ")"; + TextBuffer res = wrapOperandString(arr, false, indent, tracer); + if (arr.getExprType().arrayDim == 0) { + VarType objArr = VarType.VARTYPE_OBJECT.resizeArrayDim(1); // type family does not change + res.enclose("((" + ExprProcessor.getCastTypeName(objArr) + ")", ")"); } - return res + ".length"; + return res.append(".length"); case FUNCTION_IIF: - return wrapOperandString(lstOperands.get(0), true, indent, tracer) + " ? " + wrapOperandString(lstOperands.get(1), true, indent, tracer) + " : " + - wrapOperandString(lstOperands.get(2), true, indent, tracer); + return wrapOperandString(lstOperands.get(0), true, indent, tracer) + .append(" ? ") + .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)) + .append(" : ") + .append(wrapOperandString(lstOperands.get(2), true, indent, tracer)); case FUNCTION_IPP: - return wrapOperandString(lstOperands.get(0), true, indent, tracer) + "++"; + return wrapOperandString(lstOperands.get(0), true, indent, tracer).append("++"); case FUNCTION_PPI: - return "++" + wrapOperandString(lstOperands.get(0), true, indent, tracer); + return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("++"); case FUNCTION_IMM: - return wrapOperandString(lstOperands.get(0), true, indent, tracer) + "--"; + return wrapOperandString(lstOperands.get(0), true, indent, tracer).append("--"); case FUNCTION_MMI: - return "--" + wrapOperandString(lstOperands.get(0), true, indent, tracer); + return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("--"); case FUNCTION_INSTANCEOF: - return wrapOperandString(lstOperands.get(0), true, indent, tracer) + " instanceof " + wrapOperandString(lstOperands.get(1), true, indent, tracer); + return wrapOperandString(lstOperands.get(0), true, indent, tracer).append(" instanceof ").append(wrapOperandString(lstOperands.get(1), true, indent, tracer)); case FUNCTION_LCMP: // shouldn't appear in the final code - return "__lcmp__(" + - wrapOperandString(lstOperands.get(0), true, indent, tracer) + - "," + - wrapOperandString(lstOperands.get(1), true, indent, tracer) + - ")"; + return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("__lcmp__(") + .append(", ") + .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)) + .append(")"); case FUNCTION_FCMPL: // shouldn't appear in the final code - return "__fcmpl__(" + - wrapOperandString(lstOperands.get(0), true, indent, tracer) + - "," + - wrapOperandString(lstOperands.get(1), true, indent, tracer) + - ")"; + return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("__fcmpl__(") + .append(", ") + .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)) + .append(")"); case FUNCTION_FCMPG: // shouldn't appear in the final code - return "__fcmpg__(" + - wrapOperandString(lstOperands.get(0), true, indent, tracer) + - "," + - wrapOperandString(lstOperands.get(1), true, indent, tracer) + - ")"; + return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("__fcmpg__(") + .append(", ") + .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)) + .append(")"); case FUNCTION_DCMPL: // shouldn't appear in the final code - return "__dcmpl__(" + - wrapOperandString(lstOperands.get(0), true, indent, tracer) + - "," + - wrapOperandString(lstOperands.get(1), true, indent, tracer) + - ")"; + return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("__dcmpl__(") + .append(", ") + .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)) + .append(")"); case FUNCTION_DCMPG: // shouldn't appear in the final code - return "__dcmpg__(" + - wrapOperandString(lstOperands.get(0), true, indent, tracer) + - "," + - wrapOperandString(lstOperands.get(1), true, indent, tracer) + - ")"; + return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("__dcmpg__(") + .append(", ") + .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)) + .append(")"); } - if (functype <= FUNCTION_I2S) { - return "(" + ExprProcessor.getTypeName(types[functype - FUNCTION_I2L]) + ") " + wrapOperandString(lstOperands.get(0), true, indent, tracer); + if (funcType <= FUNCTION_I2S) { + return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("(" + ExprProcessor.getTypeName( + TYPES[funcType - FUNCTION_I2L]) + ")"); } // return ""; throw new RuntimeException("invalid function"); } + @Override public int getPrecedence() { - return getPrecedence(functype); + return getPrecedence(funcType); } public static int getPrecedence(int func) { - return precedence[func]; + return PRECEDENCE[func]; } public VarType getSimpleCastType() { - return types[functype - FUNCTION_I2L]; + return TYPES[funcType - FUNCTION_I2L]; } - private String wrapOperandString(Exprent expr, boolean eq, int indent, BytecodeMappingTracer tracer) { - + private TextBuffer wrapOperandString(Exprent expr, boolean eq, int indent, BytecodeMappingTracer tracer) { int myprec = getPrecedence(); int exprprec = expr.getPrecedence(); @@ -557,29 +550,28 @@ public class FunctionExprent extends Exprent { parentheses = (exprprec == myprec); if (parentheses) { if (expr.type == Exprent.EXPRENT_FUNCTION && - ((FunctionExprent)expr).getFunctype() == functype) { - parentheses = !associativity.contains(functype); + ((FunctionExprent)expr).getFuncType() == funcType) { + parentheses = !ASSOCIATIVITY.contains(funcType); } } } - String res = expr.toJava(indent, tracer); + TextBuffer res = expr.toJava(indent, tracer); if (parentheses) { - res = "(" + res + ")"; + res.enclose("(", ")"); } return res; } private static VarType getMaxVarType(VarType[] arr) { - - int[] types = new int[]{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_LONG}; - VarType[] vartypes = new VarType[]{VarType.VARTYPE_DOUBLE, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG}; + int[] types = {CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_LONG}; + VarType[] vartypes = {VarType.VARTYPE_DOUBLE, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG}; for (int i = 0; i < types.length; i++) { - for (int j = 0; j < arr.length; j++) { - if (arr[j].type == types[i]) { + for (VarType anArr : arr) { + if (anArr.type == types[i]) { return vartypes[i]; } } @@ -592,28 +584,33 @@ public class FunctionExprent extends Exprent { // getter and setter methods // ***************************************************************************** - public int getFunctype() { - return functype; + public int getFuncType() { + return funcType; } - public void setFunctype(int functype) { - this.functype = functype; + public void setFuncType(int funcType) { + this.funcType = funcType; } public List getLstOperands() { return lstOperands; } - public void setLstOperands(List lstOperands) { - this.lstOperands = lstOperands; - } - - public VarType getImplicitType() { - return implicitType; - } - public void setImplicitType(VarType implicitType) { this.implicitType = implicitType; } -} + // ***************************************************************************** + // IMatchable implementation + // ***************************************************************************** + + @Override + public boolean match(MatchNode matchNode, MatchEngine engine) { + if (!super.match(matchNode, engine)) { + return false; + } + + Integer type = (Integer)matchNode.getRuleValue(MatchProperties.EXPRENT_FUNCTYPE); + return type == null || this.funcType == type; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java index 4a2fbf5..32dd0ba 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java @@ -1,17 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ package org.jetbrains.java.decompiler.modules.decompiler.exps; @@ -19,11 +7,11 @@ import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.ListStack; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; - +import java.util.Set; public class IfExprent extends Exprent { @@ -46,13 +34,12 @@ public class IfExprent extends Exprent { public static final int IF_ACMPEQ = 14; public static final int IF_ACMPNE = 15; - public static final int IF_CAND = 16; - public static final int IF_COR = 17; - - public static final int IF_NOT = 18; + //public static final int IF_CAND = 16; + //public static final int IF_COR = 17; + //public static final int IF_NOT = 18; public static final int IF_VALUE = 19; - private static final int[] functypes = new int[]{ + private static final int[] FUNC_TYPES = { FunctionExprent.FUNCTION_EQ, FunctionExprent.FUNCTION_NE, FunctionExprent.FUNCTION_LT, @@ -71,70 +58,73 @@ public class IfExprent extends Exprent { FunctionExprent.FUNCTION_NE, FunctionExprent.FUNCTION_CADD, FunctionExprent.FUNCTION_COR, - FunctionExprent.FUNCTION_BOOLNOT, + FunctionExprent.FUNCTION_BOOL_NOT, -1 }; private Exprent condition; - { - this.type = EXPRENT_IF; - } + public IfExprent(int ifType, ListStack stack, Set bytecodeOffsets) { + this(null, bytecodeOffsets); - public IfExprent(int iftype, ListStack stack) { - - if (iftype <= IF_LE) { - stack.push(new ConstExprent(0, true)); + if (ifType <= IF_LE) { + stack.push(new ConstExprent(0, true, null)); } - else if (iftype <= IF_NONNULL) { - stack.push(new ConstExprent(VarType.VARTYPE_NULL, null)); + else if (ifType <= IF_NONNULL) { + stack.push(new ConstExprent(VarType.VARTYPE_NULL, null, null)); } - if (iftype == IF_VALUE) { + if (ifType == IF_VALUE) { condition = stack.pop(); } else { - condition = new FunctionExprent(functypes[iftype], stack); + condition = new FunctionExprent(FUNC_TYPES[ifType], stack, bytecodeOffsets); } } - private IfExprent(Exprent condition) { + private IfExprent(Exprent condition, Set bytecodeOffsets) { + super(EXPRENT_IF); this.condition = condition; + + addBytecodeOffsets(bytecodeOffsets); } + @Override public Exprent copy() { - return new IfExprent(condition.copy()); + return new IfExprent(condition.copy(), bytecode); } + @Override public List getAllExprents() { - List lst = new ArrayList(); + List lst = new ArrayList<>(); lst.add(condition); return lst; } @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { tracer.addMapping(bytecode); - return "if (" + condition.toJava(indent, tracer) + ")"; + return condition.toJava(indent, tracer).enclose("if (", ")"); } + @Override + public void replaceExprent(Exprent oldExpr, Exprent newExpr) { + if (oldExpr == condition) { + condition = newExpr; + } + } + + @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof IfExprent)) return false; + if (!(o instanceof IfExprent)) return false; IfExprent ie = (IfExprent)o; return InterpreterUtil.equalObjects(condition, ie.getCondition()); } - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if (oldexpr == condition) { - condition = newexpr; - } - } - public IfExprent negateIf() { - condition = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, - Arrays.asList(new Exprent[]{condition})); + condition = new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, condition, condition.bytecode); return this; } @@ -145,4 +135,4 @@ public class IfExprent extends Exprent { public void setCondition(Exprent condition) { this.condition = condition; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index 68e8cfb..3fe26b3 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -1,41 +1,34 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.exps; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; +import org.jetbrains.java.decompiler.modules.decompiler.ClasspathHelper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +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.consts.LinkConstant; +import org.jetbrains.java.decompiler.struct.consts.PooledConstant; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.match.MatchEngine; +import org.jetbrains.java.decompiler.struct.match.MatchNode; +import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.ListStack; +import org.jetbrains.java.decompiler.util.TextBuffer; +import org.jetbrains.java.decompiler.util.TextUtil; +import java.lang.reflect.Method; import java.util.*; - +import java.util.Map.Entry; public class InvocationExprent extends Exprent { @@ -49,42 +42,35 @@ public class InvocationExprent extends Exprent { public static final int TYP_INIT = 2; public static final int TYP_CLINIT = 3; - public static final int CONSTRUCTOR_NOT = 0; - public static final int CONSTRUCTOR_THIS = 1; - public static final int CONSTRUCTOR_SUPER = 2; + private static final BitSet EMPTY_BIT_SET = new BitSet(0); private String name; - private String classname; - private boolean isStatic; - + private boolean canIgnoreBoxing = true; private int functype = TYP_GENERAL; - private Exprent instance; - private MethodDescriptor descriptor; - private String stringDescriptor; - - private String invoke_dynamic_classsuffix; - + private String invokeDynamicClassSuffix; private int invocationTyp = INVOKE_VIRTUAL; - - private List lstParameters = new ArrayList(); - - { - this.type = EXPRENT_INVOCATION; - } + private List lstParameters = new ArrayList<>(); + private List bootstrapArguments; public InvocationExprent() { + super(EXPRENT_INVOCATION); } - public InvocationExprent(int opcode, LinkConstant cn, ListStack stack, int dynamic_invokation_type) { + public InvocationExprent(int opcode, + LinkConstant cn, + List bootstrapArguments, + ListStack stack, + Set bytecodeOffsets) { + this(); name = cn.elementname; classname = cn.classname; - + this.bootstrapArguments = bootstrapArguments; switch (opcode) { case CodeConstants.opc_invokestatic: invocationTyp = INVOKE_STATIC; @@ -102,31 +88,41 @@ public class InvocationExprent extends Exprent { invocationTyp = INVOKE_DYNAMIC; classname = "java/lang/Class"; // dummy class name - invoke_dynamic_classsuffix = "##Lambda_" + cn.index1 + "_" + cn.index2; + invokeDynamicClassSuffix = "##Lambda_" + cn.index1 + "_" + cn.index2; } - if ("".equals(name)) { + if (CodeConstants.INIT_NAME.equals(name)) { functype = TYP_INIT; } - else if ("".equals(name)) { + else if (CodeConstants.CLINIT_NAME.equals(name)) { functype = TYP_CLINIT; } stringDescriptor = cn.descriptor; descriptor = MethodDescriptor.parseDescriptor(cn.descriptor); - for (int i = 0; i < descriptor.params.length; i++) { + for (VarType ignored : descriptor.params) { lstParameters.add(0, stack.pop()); } if (opcode == CodeConstants.opc_invokedynamic) { - if (dynamic_invokation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic) { + int dynamicInvocationType = -1; + if (bootstrapArguments != null) { + if (bootstrapArguments.size() > 1) { // INVOKEDYNAMIC is used not only for lambdas + PooledConstant link = bootstrapArguments.get(1); + if (link instanceof LinkConstant) { + dynamicInvocationType = ((LinkConstant)link).index1; + } + } + } + if (dynamicInvocationType == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic) { isStatic = true; } else { - if (!lstParameters.isEmpty()) - instance = lstParameters - .get(0); // FIXME: remove the first parameter completely from the list. It's the object type for a virtual lambda method. + // FIXME: remove the first parameter completely from the list. It's the object type for a virtual lambda method. + if (!lstParameters.isEmpty()) { + instance = lstParameters.get(0); + } } } else if (opcode == CodeConstants.opc_invokestatic) { @@ -135,32 +131,39 @@ public class InvocationExprent extends Exprent { else { instance = stack.pop(); } + + addBytecodeOffsets(bytecodeOffsets); } private InvocationExprent(InvocationExprent expr) { + this(); + name = expr.getName(); classname = expr.getClassname(); isStatic = expr.isStatic(); + canIgnoreBoxing = expr.canIgnoreBoxing; functype = expr.getFunctype(); instance = expr.getInstance(); if (instance != null) { instance = instance.copy(); } invocationTyp = expr.getInvocationTyp(); - invoke_dynamic_classsuffix = expr.getInvokeDynamicClassSuffix(); + invokeDynamicClassSuffix = expr.getInvokeDynamicClassSuffix(); stringDescriptor = expr.getStringDescriptor(); descriptor = expr.getDescriptor(); - lstParameters = new ArrayList(expr.getLstParameters()); - for (int i = 0; i < lstParameters.size(); i++) { - lstParameters.set(i, lstParameters.get(i).copy()); - } + lstParameters = new ArrayList<>(expr.getLstParameters()); + ExprProcessor.copyEntries(lstParameters); + + addBytecodeOffsets(expr.bytecode); + bootstrapArguments = expr.getBootstrapArguments(); } - + @Override public VarType getExprType() { return descriptor.ret; } + @Override public CheckTypesResult checkExprTypeBounds() { CheckTypesResult result = new CheckTypesResult(); @@ -169,15 +172,16 @@ public class InvocationExprent extends Exprent { VarType leftType = descriptor.params[i]; - result.addMinTypeExprent(parameter, VarType.getMinTypeInFamily(leftType.type_family)); + result.addMinTypeExprent(parameter, VarType.getMinTypeInFamily(leftType.typeFamily)); result.addMaxTypeExprent(parameter, leftType); } return result; } + @Override public List getAllExprents() { - List lst = new ArrayList(); + List lst = new ArrayList<>(); if (instance != null) { lst.add(instance); } @@ -186,42 +190,54 @@ public class InvocationExprent extends Exprent { } + @Override public Exprent copy() { return new InvocationExprent(this); } @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { - StringBuilder buf = new StringBuilder(""); + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + TextBuffer buf = new TextBuffer(); String super_qualifier = null; boolean isInstanceThis = false; tracer.addMapping(bytecode); + if (instance instanceof InvocationExprent) { + ((InvocationExprent) instance).markUsingBoxingResult(); + } + if (isStatic) { + if (isBoxingCall() && canIgnoreBoxing) { + // process general "boxing" calls, e.g. 'Object[] data = { true }' or 'Byte b = 123' + // here 'byte' and 'short' values do not need an explicit narrowing type cast + ExprProcessor.getCastedExprent(lstParameters.get(0), descriptor.params[0], buf, indent, false, false, false, false, tracer); + return buf; + } + ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); if (node == null || !classname.equals(node.classStruct.qualifiedName)) { - buf.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(classname))); + buf.append(DecompilerContext.getImportCollector().getShortNameInClassContext(ExprProcessor.buildJavaClassName(classname))); } } else { if (instance != null && instance.type == Exprent.EXPRENT_VAR) { - VarExprent instvar = (VarExprent)instance; - VarVersionPaar varpaar = new VarVersionPaar(instvar); + VarExprent instVar = (VarExprent)instance; + VarVersionPair varPair = new VarVersionPair(instVar); - VarProcessor vproc = instvar.getProcessor(); - if (vproc == null) { - MethodWrapper current_meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); - if (current_meth != null) { - vproc = current_meth.varproc; + VarProcessor varProc = instVar.getProcessor(); + if (varProc == null) { + MethodWrapper currentMethod = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + if (currentMethod != null) { + varProc = currentMethod.varproc; } } String this_classname = null; - if (vproc != null) { - this_classname = vproc.getThisvars().get(varpaar); + if (varProc != null) { + this_classname = varProc.getThisVars().get(varPair); } if (this_classname != null) { @@ -229,7 +245,9 @@ public class InvocationExprent extends Exprent { if (invocationTyp == INVOKE_SPECIAL) { if (!classname.equals(this_classname)) { // TODO: direct comparison to the super class? - super_qualifier = this_classname; + StructClass cl = DecompilerContext.getStructContext().getClass(classname); + boolean isInterface = cl != null && cl.hasModifier(CodeConstants.ACC_INTERFACE); + super_qualifier = !isInterface ? this_classname : classname; } } } @@ -237,25 +255,25 @@ public class InvocationExprent extends Exprent { if (functype == TYP_GENERAL) { if (super_qualifier != null) { - StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE)).classStruct; - - if (!super_qualifier.equals(current_class.qualifiedName)) { - buf.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(super_qualifier))); - buf.append("."); - } - buf.append("super"); + TextUtil.writeQualifiedSuper(buf, super_qualifier); } - else { - String res = instance.toJava(indent, tracer); + else if (instance != null) { + TextBuffer res = instance.toJava(indent, tracer); + + if (isUnboxingCall()) { + // we don't print the unboxing call - no need to bother with the instance wrapping / casting + buf.append(res); + return buf; + } VarType rightType = instance.getExprType(); VarType leftType = new VarType(CodeConstants.TYPE_OBJECT, 0, classname); if (rightType.equals(VarType.VARTYPE_OBJECT) && !leftType.equals(rightType)) { - buf.append("((").append(ExprProcessor.getCastTypeName(leftType)).append(") "); + buf.append("((").append(ExprProcessor.getCastTypeName(leftType)).append(")"); if (instance.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) { - res = "(" + res + ")"; + res.enclose("(", ")"); } buf.append(res).append(")"); } @@ -272,7 +290,7 @@ public class InvocationExprent extends Exprent { switch (functype) { case TYP_GENERAL: if (VarExprent.VAR_NAMELESS_ENCLOSURE.equals(buf.toString())) { - buf = new StringBuilder(""); + buf = new TextBuffer(); } if (buf.length() > 0) { @@ -284,10 +302,11 @@ public class InvocationExprent extends Exprent { buf.append(""); } buf.append("("); - break; + case TYP_CLINIT: - throw new RuntimeException("Explicite invocation of "); + throw new RuntimeException("Explicit invocation of " + CodeConstants.CLINIT_NAME); + case TYP_INIT: if (super_qualifier != null) { buf.append("super("); @@ -295,113 +314,223 @@ public class InvocationExprent extends Exprent { else if (isInstanceThis) { buf.append("this("); } + else if (instance != null) { + buf.append(instance.toJava(indent, tracer)).append(".("); + } else { - buf.append(instance.toJava(indent, tracer)); - buf.append(".("); - // throw new RuntimeException("Unrecognized invocation of "); // FIXME: activate + throw new RuntimeException("Unrecognized invocation of " + CodeConstants.INIT_NAME); } } - List sigFields = null; + List mask = null; boolean isEnum = false; if (functype == TYP_INIT) { ClassNode newNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(classname); - - if (newNode != null) { // own class - if (newNode.wrapper != null) { - sigFields = newNode.wrapper.getMethodWrapper("", stringDescriptor).signatureFields; - } - else { - if (newNode.type == ClassNode.CLASS_MEMBER && (newNode.access & CodeConstants.ACC_STATIC) == 0) { // non-static member class - sigFields = new ArrayList(Collections.nCopies(lstParameters.size(), (VarVersionPaar)null)); - sigFields.set(0, new VarVersionPaar(-1, 0)); - } - } + if (newNode != null) { + mask = ExprUtil.getSyntheticParametersMask(newNode, stringDescriptor, lstParameters.size()); isEnum = newNode.classStruct.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); } } - Set setAmbiguousParameters = getAmbiguousParameters(); + BitSet setAmbiguousParameters = getAmbiguousParameters(); - boolean firstpar = true; + // omit 'new Type[] {}' for the last parameter of a vararg method call + if (lstParameters.size() == descriptor.params.length && isVarArgCall()) { + Exprent lastParam = lstParameters.get(lstParameters.size() - 1); + if (lastParam.type == EXPRENT_NEW && lastParam.getExprType().arrayDim >= 1) { + ((NewExprent) lastParam).setVarArgParam(true); + } + } + + boolean firstParameter = true; int start = isEnum ? 2 : 0; for (int i = start; i < lstParameters.size(); i++) { - if (sigFields == null || sigFields.get(i) == null) { - if (!firstpar) { - buf.append(", "); - } - + if (mask == null || mask.get(i) == null) { TextBuffer buff = new TextBuffer(); - ExprProcessor.getCastedExprent(lstParameters.get(i), descriptor.params[i], buff, indent, true, setAmbiguousParameters.contains(i), tracer); + boolean ambiguous = setAmbiguousParameters.get(i); - buf.append(buff); - firstpar = false; + // 'byte' and 'short' literals need an explicit narrowing type cast when used as a parameter + ExprProcessor.getCastedExprent(lstParameters.get(i), descriptor.params[i], buff, indent, true, ambiguous, true, true, tracer); + + // the last "new Object[0]" in the vararg call is not printed + if (buff.length() > 0) { + if (!firstParameter) { + buf.append(", "); + } + buf.append(buff); + } + + firstParameter = false; } } - buf.append(")"); - return buf.toString(); + buf.append(')'); + + return buf; } - private Set addAllSuper(Set set, String clazz) { - StructClass cstr = DecompilerContext.getStructContext().getClass(clazz); - if (cstr == null) { - return set; + private boolean isVarArgCall() { + StructClass cl = DecompilerContext.getStructContext().getClass(classname); + if (cl != null) { + StructMethod mt = cl.getMethod(InterpreterUtil.makeUniqueKey(name, stringDescriptor)); + if (mt != null) { + return mt.hasModifier(CodeConstants.ACC_VARARGS); + } } - set.add(cstr); - for (String inter : cstr.getInterfaceNames()) { - addAllSuper(set, inter); - } - addAllSuper(set, cstr.superClass.getString()); + else { + // TODO: tap into IDEA indices to access libraries methods details - return set; + // try to check the class on the classpath + Method mtd = ClasspathHelper.findMethod(classname, name, descriptor); + return mtd != null && mtd.isVarArgs(); + } + return false; } - private Set getAmbiguousParameters() { + public boolean isBoxingCall() { + if (isStatic && "valueOf".equals(name) && lstParameters.size() == 1) { + int paramType = lstParameters.get(0).getExprType().type; - Set ret = new HashSet(); + // special handling for ambiguous types + if (lstParameters.get(0).type == Exprent.EXPRENT_CONST) { + // 'Integer.valueOf(1)' has '1' type detected as TYPE_BYTECHAR + // 'Integer.valueOf(40_000)' has '40_000' type detected as TYPE_CHAR + // so we check the type family instead + if (lstParameters.get(0).getExprType().typeFamily == CodeConstants.TYPE_FAMILY_INTEGER) { + if (classname.equals("java/lang/Integer")) { + return true; + } + } - List lstMethods = new ArrayList(); - for (StructClass cstr : addAllSuper(new HashSet(), classname)) { - for (StructMethod meth : cstr.getMethods()) { - if (name.equals(meth.getName())) { - MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.getDescriptor()); - if (md.params.length == descriptor.params.length) { - boolean equals = true; - for (int i = 0; i < md.params.length; i++) { - if (md.params[i].type_family != descriptor.params[i].type_family) { - equals = false; - break; - } - } - - if (equals) { - lstMethods.add(md); - } + if (paramType == CodeConstants.TYPE_BYTECHAR || paramType == CodeConstants.TYPE_SHORTCHAR) { + if (classname.equals("java/lang/Character")) { + return true; } } } - if (lstMethods.size() > 1) { - for (int i = 0; i < descriptor.params.length; i++) { - VarType partype = descriptor.params[i]; + return classname.equals(getClassNameForPrimitiveType(paramType)); + } - for (MethodDescriptor md : lstMethods) { - if (!partype.equals(md.params[i])) { - ret.add(i); - break; + return false; + } + + public void markUsingBoxingResult() { + canIgnoreBoxing = false; + } + + // TODO: move to CodeConstants ??? + private static String getClassNameForPrimitiveType(int type) { + switch (type) { + case CodeConstants.TYPE_BOOLEAN: + return "java/lang/Boolean"; + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_BYTECHAR: + return "java/lang/Byte"; + case CodeConstants.TYPE_CHAR: + return "java/lang/Character"; + case CodeConstants.TYPE_SHORT: + case CodeConstants.TYPE_SHORTCHAR: + return "java/lang/Short"; + case CodeConstants.TYPE_INT: + return "java/lang/Integer"; + case CodeConstants.TYPE_LONG: + return "java/lang/Long"; + case CodeConstants.TYPE_FLOAT: + return "java/lang/Float"; + case CodeConstants.TYPE_DOUBLE: + return "java/lang/Double"; + } + return null; + } + + private static final Map UNBOXING_METHODS; + + static { + UNBOXING_METHODS = new HashMap<>(); + UNBOXING_METHODS.put("booleanValue", "java/lang/Boolean"); + UNBOXING_METHODS.put("byteValue", "java/lang/Byte"); + UNBOXING_METHODS.put("shortValue", "java/lang/Short"); + UNBOXING_METHODS.put("intValue", "java/lang/Integer"); + UNBOXING_METHODS.put("longValue", "java/lang/Long"); + UNBOXING_METHODS.put("floatValue", "java/lang/Float"); + UNBOXING_METHODS.put("doubleValue", "java/lang/Double"); + UNBOXING_METHODS.put("charValue", "java/lang/Character"); + } + + private boolean isUnboxingCall() { + return !isStatic && lstParameters.size() == 0 && classname.equals(UNBOXING_METHODS.get(name)); + } + + private BitSet getAmbiguousParameters() { + StructClass cl = DecompilerContext.getStructContext().getClass(classname); + if (cl == null) return EMPTY_BIT_SET; + + // check number of matches + List matches = new ArrayList<>(); + nextMethod: + for (StructMethod mt : cl.getMethods()) { + if (name.equals(mt.getName())) { + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + if (md.params.length == descriptor.params.length) { + for (int i = 0; i < md.params.length; i++) { + if (md.params[i].typeFamily != descriptor.params[i].typeFamily) { + continue nextMethod; } } + matches.add(md); } } } + if (matches.size() == 1) return EMPTY_BIT_SET; - return ret; + // check if a call is unambiguous + StructMethod mt = cl.getMethod(InterpreterUtil.makeUniqueKey(name, stringDescriptor)); + if (mt != null) { + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + if (md.params.length == lstParameters.size()) { + boolean exact = true; + for (int i = 0; i < md.params.length; i++) { + if (!md.params[i].equals(lstParameters.get(i).getExprType())) { + exact = false; + break; + } + } + if (exact) return EMPTY_BIT_SET; + } + } + + // mark parameters + BitSet ambiguous = new BitSet(descriptor.params.length); + for (int i = 0; i < descriptor.params.length; i++) { + VarType paramType = descriptor.params[i]; + for (MethodDescriptor md : matches) { + if (!paramType.equals(md.params[i])) { + ambiguous.set(i); + break; + } + } + } + return ambiguous; } + @Override + public void replaceExprent(Exprent oldExpr, Exprent newExpr) { + if (oldExpr == instance) { + instance = newExpr; + } + + for (int i = 0; i < lstParameters.size(); i++) { + if (oldExpr == lstParameters.get(i)) { + lstParameters.set(i, newExpr); + } + } + } + + @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof InvocationExprent)) return false; + if (!(o instanceof InvocationExprent)) return false; InvocationExprent it = (InvocationExprent)o; return InterpreterUtil.equalObjects(name, it.getName()) && @@ -413,18 +542,6 @@ public class InvocationExprent extends Exprent { InterpreterUtil.equalLists(lstParameters, it.getLstParameters()); } - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if (oldexpr == instance) { - instance = newexpr; - } - - for (int i = 0; i < lstParameters.size(); i++) { - if (oldexpr == lstParameters.get(i)) { - lstParameters.set(i, newexpr); - } - } - } - public List getLstParameters() { return lstParameters; } @@ -493,11 +610,46 @@ public class InvocationExprent extends Exprent { return invocationTyp; } - public void setInvocationTyp(int invocationTyp) { - this.invocationTyp = invocationTyp; + public String getInvokeDynamicClassSuffix() { + return invokeDynamicClassSuffix; } - public String getInvokeDynamicClassSuffix() { - return invoke_dynamic_classsuffix; + public List getBootstrapArguments() { + return bootstrapArguments; } -} + + // ***************************************************************************** + // IMatchable implementation + // ***************************************************************************** + + @Override + public boolean match(MatchNode matchNode, MatchEngine engine) { + if (!super.match(matchNode, engine)) { + return false; + } + + for (Entry rule : matchNode.getRules().entrySet()) { + RuleValue value = rule.getValue(); + + MatchProperties key = rule.getKey(); + if (key == MatchProperties.EXPRENT_INVOCATION_PARAMETER) { + if (value.isVariable() && (value.parameter >= lstParameters.size() || + !engine.checkAndSetVariableValue(value.value.toString(), lstParameters.get(value.parameter)))) { + return false; + } + } + else if (key == MatchProperties.EXPRENT_INVOCATION_CLASS) { + if (!value.value.equals(this.classname)) { + return false; + } + } + else if (key == MatchProperties.EXPRENT_INVOCATION_SIGNATURE) { + if (!value.value.equals(this.name + this.stringDescriptor)) { + return false; + } + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java index 5a71ce3..4172874 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java @@ -1,85 +1,75 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ package org.jetbrains.java.decompiler.modules.decompiler.exps; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.ArrayList; import java.util.List; - +import java.util.Set; public class MonitorExprent extends Exprent { public static final int MONITOR_ENTER = 0; public static final int MONITOR_EXIT = 1; - private int montype; - + private final int monType; private Exprent value; - { - this.type = EXPRENT_MONITOR; - } - - public MonitorExprent(int montype, Exprent value) { - this.montype = montype; + public MonitorExprent(int monType, Exprent value, Set bytecodeOffsets) { + super(EXPRENT_MONITOR); + this.monType = monType; this.value = value; + + addBytecodeOffsets(bytecodeOffsets); } + @Override public Exprent copy() { - return new MonitorExprent(montype, value.copy()); + return new MonitorExprent(monType, value.copy(), bytecode); } + @Override public List getAllExprents() { - List lst = new ArrayList(); + List lst = new ArrayList<>(); lst.add(value); return lst; } @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { - + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { tracer.addMapping(bytecode); - if (montype == MONITOR_ENTER) { - return "synchronized (" + value.toJava(indent, tracer) + ")"; + if (monType == MONITOR_ENTER) { + return value.toJava(indent, tracer).enclose("synchronized(", ")"); } else { - return ""; + return new TextBuffer(); } } + @Override + public void replaceExprent(Exprent oldExpr, Exprent newExpr) { + if (oldExpr == value) { + value = newExpr; + } + } + + @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof MonitorExprent)) return false; + if (!(o instanceof MonitorExprent)) return false; MonitorExprent me = (MonitorExprent)o; - return montype == me.getMontype() && + return monType == me.getMonType() && InterpreterUtil.equalObjects(value, me.getValue()); } - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if (oldexpr == value) { - value = newexpr; - } - } - - public int getMontype() { - return montype; + public int getMonType() { + return monType; } public Exprent getValue() { 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 8f8f684..a1d4c4c 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java @@ -1,262 +1,228 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.exps; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassWriter; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; +import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericClassDescriptor; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.ListStack; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.Set; public class NewExprent extends Exprent { - private InvocationExprent constructor; - - private VarType newtype; - - private List lstDims = new ArrayList(); - - private List lstArrayElements = new ArrayList(); - + private final VarType newType; + private final List lstDims; + private List lstArrayElements = new ArrayList<>(); private boolean directArrayInit; - + private boolean isVarArgParam; private boolean anonymous; - private boolean lambda; + private boolean enumConst; - private boolean enumconst; - - { - this.type = EXPRENT_NEW; + public NewExprent(VarType newType, ListStack stack, int arrayDim, Set bytecodeOffsets) { + this(newType, getDimensions(arrayDim, stack), bytecodeOffsets); } - public NewExprent(VarType newtype, ListStack stack, int arraydim) { - this.newtype = newtype; - for (int i = 0; i < arraydim; i++) { - lstDims.add(0, stack.pop()); - } - - setAnonymous(); - } - - public NewExprent(VarType newtype, List lstDims) { - this.newtype = newtype; + public NewExprent(VarType newType, List lstDims, Set bytecodeOffsets) { + super(EXPRENT_NEW); + this.newType = newType; this.lstDims = lstDims; - setAnonymous(); - } - - private void setAnonymous() { - anonymous = false; lambda = false; - - if (newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0) { - ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(newtype.value); - + if (newType.type == CodeConstants.TYPE_OBJECT && newType.arrayDim == 0) { + ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(newType.value); if (node != null && (node.type == ClassNode.CLASS_ANONYMOUS || node.type == ClassNode.CLASS_LAMBDA)) { anonymous = true; - if (node.type == ClassNode.CLASS_LAMBDA) { lambda = true; } } } + + addBytecodeOffsets(bytecodeOffsets); } + private static List getDimensions(int arrayDim, ListStack stack) { + List lstDims = new ArrayList<>(); + for (int i = 0; i < arrayDim; i++) { + lstDims.add(0, stack.pop()); + } + return lstDims; + } + + public boolean isVarArgParam() { + return isVarArgParam; + } + + @Override public VarType getExprType() { - - if (anonymous) { - ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(newtype.value); - - return node.anonymousClassType; - } - else { - return newtype; - } + return anonymous ? DecompilerContext.getClassProcessor().getMapRootClasses().get(newType.value).anonymousClassType : newType; } + @Override public CheckTypesResult checkExprTypeBounds() { CheckTypesResult result = new CheckTypesResult(); - if (newtype.arraydim != 0) { + if (newType.arrayDim != 0) { for (Exprent dim : lstDims) { result.addMinTypeExprent(dim, VarType.VARTYPE_BYTECHAR); result.addMaxTypeExprent(dim, VarType.VARTYPE_INT); } - if (newtype.arraydim == 1) { - - VarType leftType = newtype.copy(); - leftType.decArrayDim(); - + if (newType.arrayDim == 1) { + VarType leftType = newType.decreaseArrayDim(); for (Exprent element : lstArrayElements) { - result.addMinTypeExprent(element, VarType.getMinTypeInFamily(leftType.type_family)); + result.addMinTypeExprent(element, VarType.getMinTypeInFamily(leftType.typeFamily)); result.addMaxTypeExprent(element, leftType); } } } - else { - if (constructor != null) { - return constructor.checkExprTypeBounds(); - } + else if (constructor != null) { + return constructor.checkExprTypeBounds(); } return result; } + @Override public List getAllExprents() { - List lst = new ArrayList(); - if (newtype.arraydim == 0) { - if (constructor != null) { - Exprent constructor_instance = constructor.getInstance(); + List lst = new ArrayList<>(); - if (constructor_instance != null) { // should be true only for a lambda expression with a virtual content method - lst.add(constructor_instance); - } - - lst.addAll(constructor.getLstParameters()); - } - } - else { + if (newType.arrayDim != 0) { lst.addAll(lstDims); lst.addAll(lstArrayElements); } + else if (constructor != null) { + Exprent constructor = this.constructor.getInstance(); + if (constructor != null) { // should be true only for a lambda expression with a virtual content method + lst.add(constructor); + } + lst.addAll(this.constructor.getLstParameters()); + } return lst; } + @Override public Exprent copy() { - List lst = new ArrayList(); + List lst = new ArrayList<>(); for (Exprent expr : lstDims) { lst.add(expr.copy()); } - NewExprent ret = new NewExprent(newtype, lst); + NewExprent ret = new NewExprent(newType, lst, bytecode); ret.setConstructor(constructor == null ? null : (InvocationExprent)constructor.copy()); ret.setLstArrayElements(lstArrayElements); ret.setDirectArrayInit(directArrayInit); ret.setAnonymous(anonymous); - ret.setEnumconst(enumconst); + ret.setEnumConst(enumConst); return ret; } + @Override public int getPrecedence() { return 1; // precedence of new } @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { TextBuffer buf = new TextBuffer(); if (anonymous) { + ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(newType.value); - ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(newtype.value); + boolean selfReference = DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE) == child; - buf.append("("); + // IDEA-204310 - avoid backtracking later on for lambdas (causes spurious imports) + if (!enumConst && (!lambda || DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS))) { + String enclosing = null; - if (!lambda && constructor != null) { - - InvocationExprent invsuper = child.superInvocation; - - ClassNode newnode = DecompilerContext.getClassProcessor().getMapRootClasses().get(invsuper.getClassname()); - - List sigFields = null; - if (newnode != null) { // own class - if (newnode.wrapper != null) { - sigFields = newnode.wrapper.getMethodWrapper("", invsuper.getStringDescriptor()).signatureFields; + if (!lambda && constructor != null) { + enclosing = getQualifiedNewInstance(child.anonymousClassType.value, constructor.getLstParameters(), indent, tracer); + if (enclosing != null) { + buf.append(enclosing).append('.'); } - else { - if (newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 && - !constructor.getLstParameters().isEmpty()) { // member non-static class invoked with enclosing class instance - sigFields = new ArrayList(Collections.nCopies(constructor.getLstParameters().size(), (VarVersionPaar)null)); - sigFields.set(0, new VarVersionPaar(-1, 0)); + } + + buf.append("new "); + + if (selfReference) { + buf.append(""); + } else { + String typename = ExprProcessor.getCastTypeName(child.anonymousClassType); + if (enclosing != null) { + ClassNode anonymousNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(child.anonymousClassType.value); + if (anonymousNode != null) { + typename = anonymousNode.simpleName; + } + else { + typename = typename.substring(typename.lastIndexOf('.') + 1); } } + + GenericClassDescriptor descriptor = ClassWriter.getGenericClassDescriptor(child.classStruct); + if (descriptor != null) { + if (descriptor.superinterfaces.isEmpty()) { + buf.append(GenericMain.getGenericCastTypeName(descriptor.superclass)); + } + else { + if (descriptor.superinterfaces.size() > 1 && !lambda) { + DecompilerContext.getLogger().writeMessage("Inconsistent anonymous class signature: " + child.classStruct.qualifiedName, + IFernflowerLogger.Severity.WARN); + } + buf.append(GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(0))); + } + } + else { + buf.append(typename); + } + } + } + + buf.append('('); + + if (!lambda && constructor != null) { + List parameters = constructor.getLstParameters(); + List mask = child.getWrapper().getMethodWrapper(CodeConstants.INIT_NAME, constructor.getStringDescriptor()).synthParameters; + if (mask == null) { + InvocationExprent superCall = child.superInvocation; + mask = ExprUtil.getSyntheticParametersMask(superCall.getClassname(), superCall.getStringDescriptor(), parameters.size()); } - boolean firstpar = true; - int start = 0, end = invsuper.getLstParameters().size(); - if (enumconst) { - start += 2; - end -= 1; - } - for (int i = start; i < end; i++) { - if (sigFields == null || sigFields.get(i) == null) { - if (!firstpar) { + int start = enumConst ? 2 : 0; + boolean firstParam = true; + for (int i = start; i < parameters.size(); i++) { + if (mask == null || mask.get(i) == null) { + if (!firstParam) { buf.append(", "); } - Exprent param = invsuper.getLstParameters().get(i); - if (param.type == Exprent.EXPRENT_VAR) { - int varindex = ((VarExprent)param).getIndex(); - if (varindex > 0 && varindex <= constructor.getLstParameters().size()) { - param = constructor.getLstParameters().get(varindex - 1); - } - } + ExprProcessor.getCastedExprent(parameters.get(i), constructor.getDescriptor().params[i], buf, indent, true, tracer); - TextBuffer buff = new TextBuffer(); - ExprProcessor.getCastedExprent(param, invsuper.getDescriptor().params[i], buff, indent, true, tracer); - - buf.append(buff); - firstpar = false; + firstParam = false; } } } - if (!enumconst) { - String enclosing = null; - if (!lambda && constructor != null) { - enclosing = getQualifiedNewInstance(child.anonymousClassType.value, constructor.getLstParameters(), indent, tracer); - } + buf.append(')'); - String typename = ExprProcessor.getCastTypeName(child.anonymousClassType); - - if (enclosing != null) { - ClassNode anonimousNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(child.anonymousClassType.value); - if (anonimousNode != null) { - typename = anonimousNode.simpleName; - } - else { - typename = typename.substring(typename.lastIndexOf('.') + 1); - } - } - buf.insert(0, "new " + typename); - - if (enclosing != null) { - buf.insert(0, enclosing + "."); - } - } - - buf.append(")"); - - if (enumconst && buf.length() == 2) { + if (enumConst && buf.length() == 2) { buf.setLength(0); } @@ -265,132 +231,153 @@ public class NewExprent extends Exprent { buf.setLength(0); // remove the usual 'new ()', it will be replaced with lambda style '() ->' } Exprent methodObject = constructor == null ? null : constructor.getInstance(); - new ClassWriter().classLambdaToJava(child, buf, methodObject, indent); + TextBuffer clsBuf = new TextBuffer(); + new ClassWriter().classLambdaToJava(child, clsBuf, methodObject, indent, tracer); + buf.append(clsBuf); + tracer.incrementCurrentSourceLine(clsBuf.countLines()); } - else { - new ClassWriter().classToJava(child, buf, indent); + else if (!selfReference) { + TextBuffer clsBuf = new TextBuffer(); + new ClassWriter().classToJava(child, clsBuf, indent, tracer); + buf.append(clsBuf); + tracer.incrementCurrentSourceLine(clsBuf.countLines()); } } else if (directArrayInit) { - VarType leftType = newtype.copy(); - leftType.decArrayDim(); - - buf.append("{ "); + VarType leftType = newType.decreaseArrayDim(); + buf.append('{'); for (int i = 0; i < lstArrayElements.size(); i++) { if (i > 0) { buf.append(", "); } ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buf, indent, false, tracer); } - buf.append("}"); + buf.append('}'); } - else { - if (newtype.arraydim == 0) { + else if (newType.arrayDim == 0) { + if (!enumConst) { + String enclosing = null; if (constructor != null) { - - List lstParameters = constructor.getLstParameters(); - - ClassNode newnode = DecompilerContext.getClassProcessor().getMapRootClasses().get(constructor.getClassname()); - - List sigFields = null; - if (newnode != null) { // own class - if (newnode.wrapper != null) { - sigFields = newnode.wrapper.getMethodWrapper("", constructor.getStringDescriptor()).signatureFields; - } - else { - if (newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 && - !constructor.getLstParameters().isEmpty()) { // member non-static class invoked with enclosing class instance - sigFields = new ArrayList(Collections.nCopies(lstParameters.size(), (VarVersionPaar)null)); - sigFields.set(0, new VarVersionPaar(-1, 0)); - } - } - } - - int start = enumconst ? 2 : 0; - if (!enumconst || start < lstParameters.size()) { - buf.append("("); - - boolean firstpar = true; - for (int i = start; i < lstParameters.size(); i++) { - if (sigFields == null || sigFields.get(i) == null) { - if (!firstpar) { - buf.append(", "); - } - - TextBuffer buff = new TextBuffer(); - ExprProcessor.getCastedExprent(lstParameters.get(i), constructor.getDescriptor().params[i], buff, indent, true, tracer); - - buf.append(buff); - firstpar = false; - } - } - buf.append(")"); + enclosing = getQualifiedNewInstance(newType.value, constructor.getLstParameters(), indent, tracer); + if (enclosing != null) { + buf.append(enclosing).append('.'); } } - if (!enumconst) { - String enclosing = null; - if (constructor != null) { - enclosing = getQualifiedNewInstance(newtype.value, constructor.getLstParameters(), indent, tracer); + buf.append("new "); + + String typename = ExprProcessor.getTypeName(newType); + if (enclosing != null) { + ClassNode newNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(newType.value); + if (newNode != null) { + typename = newNode.simpleName; } + else { + typename = typename.substring(typename.lastIndexOf('.') + 1); + } + } + buf.append(typename); + } - String typename = ExprProcessor.getTypeName(newtype); + if (constructor != null) { + List parameters = constructor.getLstParameters(); + List mask = ExprUtil.getSyntheticParametersMask(constructor.getClassname(), constructor.getStringDescriptor(), parameters.size()); - if (enclosing != null) { - ClassNode newNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(newtype.value); - if (newNode != null) { - typename = newNode.simpleName; - } - else { - typename = typename.substring(typename.lastIndexOf('.') + 1); + int start = enumConst ? 2 : 0; + if (!enumConst || start < parameters.size()) { + buf.append('('); + + boolean firstParam = true; + for (int i = start; i < parameters.size(); i++) { + if (mask == null || mask.get(i) == null) { + Exprent expr = parameters.get(i); + VarType leftType = constructor.getDescriptor().params[i]; + + if (i == parameters.size() - 1 && expr.getExprType() == VarType.VARTYPE_NULL && probablySyntheticParameter(leftType.value)) { + break; // skip last parameter of synthetic constructor call + } + + if (!firstParam) { + buf.append(", "); + } + + ExprProcessor.getCastedExprent(expr, leftType, buf, indent, true, false, true, true, tracer); + + firstParam = false; } } - buf.insert(0, "new " + typename); - if (enclosing != null) { - buf.insert(0, enclosing + "."); + buf.append(')'); + } + } + } + else if (isVarArgParam) { + // just print the array elements + VarType leftType = newType.decreaseArrayDim(); + for (int i = 0; i < lstArrayElements.size(); i++) { + if (i > 0) { + buf.append(", "); + } + + // new String[][]{{"abc"}, {"DEF"}} => new String[]{"abc"}, new String[]{"DEF"} + Exprent element = lstArrayElements.get(i); + if (element.type == EXPRENT_NEW) { + ((NewExprent) element).setDirectArrayInit(false); + } + ExprProcessor.getCastedExprent(element, leftType, buf, indent, false, tracer); + } + + // if there is just one element of Object[] type it needs to be casted to resolve ambiguity + if (lstArrayElements.size() == 1) { + VarType elementType = lstArrayElements.get(0).getExprType(); + if (elementType.type == CodeConstants.TYPE_OBJECT && elementType.value.equals("java/lang/Object") && elementType.arrayDim >= 1) { + buf.prepend("(Object)"); + } + } + } + else { + buf.append("new ").append(ExprProcessor.getTypeName(newType)); + + if (lstArrayElements.isEmpty()) { + for (int i = 0; i < newType.arrayDim; i++) { + buf.append('['); + if (i < lstDims.size()) { + buf.append(lstDims.get(i).toJava(indent, tracer)); } + buf.append(']'); } } else { - buf.append("new ").append(ExprProcessor.getTypeName(newtype)); - - if (lstArrayElements.isEmpty()) { - for (int i = 0; i < newtype.arraydim; i++) { - buf.append("[").append(i < lstDims.size() ? lstDims.get(i).toJava(indent, tracer) : "").append("]"); - } + for (int i = 0; i < newType.arrayDim; i++) { + buf.append("[]"); } - else { - for (int i = 0; i < newtype.arraydim; i++) { - buf.append("[]"); + + VarType leftType = newType.decreaseArrayDim(); + buf.append('{'); + for (int i = 0; i < lstArrayElements.size(); i++) { + if (i > 0) { + buf.append(", "); } - - VarType leftType = newtype.copy(); - leftType.decArrayDim(); - - buf.append(" { "); - for (int i = 0; i < lstArrayElements.size(); i++) { - if (i > 0) { - buf.append(", "); - } - TextBuffer buff = new TextBuffer(); - ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buff, indent, false, tracer); - - buf.append(buff); - } - buf.append("}"); + ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buf, indent, false, tracer); } + buf.append('}'); } } - return buf.toString(); + + return buf; + } + + private static boolean probablySyntheticParameter(String className) { + ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(className); + return node != null && node.type == ClassNode.CLASS_ANONYMOUS; } private static String getQualifiedNewInstance(String classname, List lstParams, int indent, BytecodeMappingTracer tracer) { - ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(classname); - if (node != null && node.type != ClassNode.CLASS_ROOT && (node.access & CodeConstants.ACC_STATIC) == 0) { + if (node != null && node.type != ClassNode.CLASS_ROOT && node.type != ClassNode.CLASS_LOCAL + && (node.access & CodeConstants.ACC_STATIC) == 0) { if (!lstParams.isEmpty()) { Exprent enclosing = lstParams.get(0); @@ -400,7 +387,7 @@ public class NewExprent extends Exprent { VarExprent varEnclosing = (VarExprent)enclosing; StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE)).classStruct; - String this_classname = varEnclosing.getProcessor().getThisvars().get(new VarVersionPaar(varEnclosing)); + String this_classname = varEnclosing.getProcessor().getThisVars().get(new VarVersionPair(varEnclosing)); if (!current_class.qualifiedName.equals(this_classname)) { isQualifiedNew = true; @@ -411,7 +398,7 @@ public class NewExprent extends Exprent { } if (isQualifiedNew) { - return enclosing.toJava(indent, tracer); + return enclosing.toJava(indent, tracer).toString(); } } } @@ -419,40 +406,42 @@ public class NewExprent extends Exprent { return null; } - public boolean equals(Object o) { - if (o == this) return true; - if (o == null || !(o instanceof NewExprent)) return false; - - NewExprent ne = (NewExprent)o; - return InterpreterUtil.equalObjects(newtype, ne.getNewtype()) && - InterpreterUtil.equalLists(lstDims, ne.getLstDims()) && - InterpreterUtil.equalObjects(constructor, ne.getConstructor()) && - directArrayInit == ne.directArrayInit && - InterpreterUtil.equalLists(lstArrayElements, ne.getLstArrayElements()); - } - - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if (oldexpr == constructor) { - constructor = (InvocationExprent)newexpr; + @Override + public void replaceExprent(Exprent oldExpr, Exprent newExpr) { + if (oldExpr == constructor) { + constructor = (InvocationExprent)newExpr; } if (constructor != null) { - constructor.replaceExprent(oldexpr, newexpr); + constructor.replaceExprent(oldExpr, newExpr); } for (int i = 0; i < lstDims.size(); i++) { - if (oldexpr == lstDims.get(i)) { - lstDims.set(i, newexpr); + if (oldExpr == lstDims.get(i)) { + lstDims.set(i, newExpr); } } for (int i = 0; i < lstArrayElements.size(); i++) { - if (oldexpr == lstArrayElements.get(i)) { - lstArrayElements.set(i, newexpr); + if (oldExpr == lstArrayElements.get(i)) { + lstArrayElements.set(i, newExpr); } } } + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof NewExprent)) return false; + + NewExprent ne = (NewExprent)o; + return InterpreterUtil.equalObjects(newType, ne.getNewType()) && + InterpreterUtil.equalLists(lstDims, ne.getLstDims()) && + InterpreterUtil.equalObjects(constructor, ne.getConstructor()) && + directArrayInit == ne.directArrayInit && + InterpreterUtil.equalLists(lstArrayElements, ne.getLstArrayElements()); + } + public InvocationExprent getConstructor() { return constructor; } @@ -465,8 +454,8 @@ public class NewExprent extends Exprent { return lstDims; } - public VarType getNewtype() { - return newtype; + public VarType getNewType() { + return newType; } public List getLstArrayElements() { @@ -477,14 +466,14 @@ public class NewExprent extends Exprent { this.lstArrayElements = lstArrayElements; } - public boolean isDirectArrayInit() { - return directArrayInit; - } - public void setDirectArrayInit(boolean directArrayInit) { this.directArrayInit = directArrayInit; } + public void setVarArgParam(boolean isVarArgParam) { + this.isVarArgParam = isVarArgParam; + } + public boolean isLambda() { return lambda; } @@ -497,11 +486,7 @@ public class NewExprent extends Exprent { this.anonymous = anonymous; } - public boolean isEnumconst() { - return enumconst; - } - - public void setEnumconst(boolean enumconst) { - this.enumconst = enumconst; + public void setEnumConst(boolean enumConst) { + this.enumConst = enumConst; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java index 828d06a..3a97175 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java @@ -1,17 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ package org.jetbrains.java.decompiler.modules.decompiler.exps; @@ -19,55 +7,57 @@ import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.ArrayList; import java.util.List; - +import java.util.Set; public class SwitchExprent extends Exprent { private Exprent value; + private List> caseValues = new ArrayList<>(); - private List> caseValues = new ArrayList>(); - - { - this.type = EXPRENT_SWITCH; - } - - public SwitchExprent(Exprent value) { + public SwitchExprent(Exprent value, Set bytecodeOffsets) { + super(EXPRENT_SWITCH); this.value = value; + + addBytecodeOffsets(bytecodeOffsets); } + @Override public Exprent copy() { - SwitchExprent swexpr = new SwitchExprent(value.copy()); + SwitchExprent swExpr = new SwitchExprent(value.copy(), bytecode); - List> lstCaseValues = new ArrayList>(); - for (List lst : caseValues) { - lstCaseValues.add(new ArrayList(lst)); + List> lstCaseValues = new ArrayList<>(); + for (List lst : caseValues) { + lstCaseValues.add(new ArrayList<>(lst)); } - swexpr.setCaseValues(lstCaseValues); + swExpr.setCaseValues(lstCaseValues); - return swexpr; + return swExpr; } + @Override public VarType getExprType() { return value.getExprType(); } + @Override public CheckTypesResult checkExprTypeBounds() { CheckTypesResult result = new CheckTypesResult(); result.addMinTypeExprent(value, VarType.VARTYPE_BYTECHAR); result.addMaxTypeExprent(value, VarType.VARTYPE_INT); - VarType valtype = value.getExprType(); - for (List lst : caseValues) { - for (ConstExprent expr : lst) { + VarType valType = value.getExprType(); + for (List lst : caseValues) { + for (Exprent expr : lst) { if (expr != null) { - VarType casetype = expr.getExprType(); - if (!casetype.equals(valtype)) { - valtype = VarType.getCommonSupertype(casetype, valtype); - result.addMinTypeExprent(value, valtype); + VarType caseType = expr.getExprType(); + if (!caseType.equals(valType)) { + valType = VarType.getCommonSupertype(caseType, valType); + result.addMinTypeExprent(value, valType); } } } @@ -76,24 +66,33 @@ public class SwitchExprent extends Exprent { return result; } + @Override public List getAllExprents() { - List lst = new ArrayList(); + List lst = new ArrayList<>(); lst.add(value); return lst; } @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { tracer.addMapping(bytecode); - return "switch (" + value.toJava(indent, tracer) + ")"; + return value.toJava(indent, tracer).enclose("switch(", ")"); } + @Override + public void replaceExprent(Exprent oldExpr, Exprent newExpr) { + if (oldExpr == value) { + value = newExpr; + } + } + + @Override public boolean equals(Object o) { if (o == this) { return true; } - if (o == null || !(o instanceof SwitchExprent)) { + if (!(o instanceof SwitchExprent)) { return false; } @@ -101,17 +100,11 @@ public class SwitchExprent extends Exprent { return InterpreterUtil.equalObjects(value, sw.getValue()); } - public void replaceExprent(Exprent oldexpr, Exprent newexpr) { - if (oldexpr == value) { - value = newexpr; - } - } - public Exprent getValue() { return value; } - public void setCaseValues(List> caseValues) { + public void setCaseValues(List> caseValues) { this.caseValues = caseValues; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/TypeAnnotation.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/TypeAnnotation.java new file mode 100644 index 0000000..0836731 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/TypeAnnotation.java @@ -0,0 +1,53 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.modules.decompiler.exps; + +public class TypeAnnotation { + public static final int CLASS_TYPE_PARAMETER = 0x00; + public static final int METHOD_TYPE_PARAMETER = 0x01; + public static final int SUPER_TYPE_REFERENCE = 0x10; + public static final int CLASS_TYPE_PARAMETER_BOUND = 0x11; + public static final int METHOD_TYPE_PARAMETER_BOUND = 0x12; + public static final int FIELD = 0x13; + public static final int METHOD_RETURN_TYPE = 0x14; + public static final int METHOD_RECEIVER = 0x15; + public static final int METHOD_PARAMETER = 0x16; + public static final int THROWS_REFERENCE = 0x17; + public static final int LOCAL_VARIABLE = 0x40; + public static final int RESOURCE_VARIABLE = 0x41; + public static final int CATCH_CLAUSE = 0x42; + public static final int EXPR_INSTANCEOF = 0x43; + public static final int EXPR_NEW = 0x44; + public static final int EXPR_CONSTRUCTOR_REF = 0x45; + public static final int EXPR_METHOD_REF = 0x46; + public static final int TYPE_ARG_CAST = 0x47; + public static final int TYPE_ARG_CONSTRUCTOR_CALL = 0x48; + public static final int TYPE_ARG_METHOD_CALL = 0x49; + public static final int TYPE_ARG_CONSTRUCTOR_REF = 0x4A; + public static final int TYPE_ARG_METHOD_REF = 0x4B; + + private final int target; + private final byte[] path; + private final AnnotationExprent annotation; + + public TypeAnnotation(int target, byte[] path, AnnotationExprent annotation) { + this.target = target; + this.path = path; + this.annotation = annotation; + } + + public int getTargetType() { + return target >> 24; + } + + public int getIndex() { + return target & 0x0FFFF; + } + + public boolean isTopLevel() { + return path == null; + } + + public AnnotationExprent getAnnotation() { + return annotation; + } +} \ No newline at end of file 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 b8e60e7..0ad0a20 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -1,125 +1,183 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; import org.jetbrains.java.decompiler.main.ClassWriter; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; -import org.jetbrains.java.decompiler.main.TextBuffer; -import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTypeTableAttribute; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; +import org.jetbrains.java.decompiler.struct.match.MatchEngine; +import org.jetbrains.java.decompiler.struct.match.MatchNode; +import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue; import org.jetbrains.java.decompiler.util.InterpreterUtil; -import org.jetbrains.java.decompiler.util.SortUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; +import org.jetbrains.java.decompiler.util.TextUtil; import java.util.ArrayList; import java.util.List; -public class VarExprent extends Exprent implements SortUtil.Indexed { // Spigot - +public class VarExprent extends Exprent { public static final int STACK_BASE = 10000; - public static final String VAR_NAMELESS_ENCLOSURE = ""; - private int index; - - private VarType vartype; - + private int index; + private VarType varType; private boolean definition = false; - - private VarProcessor processor; - + private final VarProcessor processor; + private final int visibleOffset; private int version = 0; - - private boolean classdef = false; - + private boolean classDef = false; private boolean stack = false; + private String name; - { - this.type = EXPRENT_VAR; + public VarExprent(int index, VarType varType, VarProcessor processor) { + this(index, varType, processor, -1); } - public VarExprent(int index, VarType vartype, VarProcessor processor) { + public VarExprent(int index, VarType varType, VarProcessor processor, int visibleOffset) { + super(EXPRENT_VAR); this.index = index; - this.vartype = vartype; + this.varType = varType; this.processor = processor; + this.visibleOffset = visibleOffset; + } + @Override public VarType getExprType() { - return getVartype(); + return getVarType(); } + @Override public int getExprentUse() { return Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE; } + @Override public List getAllExprents() { - return new ArrayList(); + return new ArrayList<>(); } + @Override public Exprent copy() { - VarExprent var = new VarExprent(index, getVartype(), processor); + VarExprent var = new VarExprent(index, getVarType(), processor, visibleOffset); var.setDefinition(definition); var.setVersion(version); - var.setClassdef(classdef); + var.setClassDef(classDef); var.setStack(stack); return var; } @Override - public String toJava(int indent, BytecodeMappingTracer tracer) { + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { TextBuffer buffer = new TextBuffer(); tracer.addMapping(bytecode); - if (classdef) { - ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(vartype.value); - new ClassWriter().classToJava(child, buffer, indent); + if (classDef) { + ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(varType.value); + new ClassWriter().classToJava(child, buffer, indent, tracer); + tracer.incrementCurrentSourceLine(buffer.countLines()); } else { - String name = null; - String typeName = ExprProcessor.getCastTypeName(getVartype()); // Spigot - if (processor != null) { - name = processor.getVarName(new VarVersionPaar(index, version, typeName, false)); // Spigot - } + VarVersionPair varVersion = getVarVersionPair(); + if (definition) { - if (processor != null && processor.getVarFinal(new VarVersionPaar(index, version)) == VarTypeProcessor.VAR_FINALEXPLICIT) { + if (processor != null && processor.getVarFinal(varVersion) == VarTypeProcessor.VAR_EXPLICIT_FINAL) { buffer.append("final "); } - buffer.append(typeName).append(" "); // Spigot + appendDefinitionType(buffer); + buffer.append(" "); } - buffer.append(name == null ? ("var" + index + (version == 0 ? "" : "_" + version)) : name); + setIndex(this.index); + buffer.append(name == null ? ("var_ree" + index + (this.version == 0 ? "" : "_" + this.version)) : name); } - return buffer.toString(); + return buffer; } + public VarVersionPair getVarVersionPair() { + return new VarVersionPair(index, version); + } + + public String getDebugName(StructMethod method) { + StructLocalVariableTableAttribute attr = method.getLocalVariableAttr(); + if (attr != null && processor != null) { + Integer origIndex = processor.getVarOriginalIndex(index); + if (origIndex != null) { + String name = attr.getName(origIndex, visibleOffset); + if (name != null && TextUtil.isValidIdentifier(name, method.getBytecodeVersion())) { + return name; + } + } + } + return null; + } + + private void appendDefinitionType(TextBuffer buffer) { + if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) { + MethodWrapper method = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + if (method != null) { + Integer originalIndex = null; + if (processor != null) { + originalIndex = processor.getVarOriginalIndex(index); + } + if (originalIndex != null) { + // first try from signature + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructLocalVariableTypeTableAttribute attr = + method.methodStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE); + if (attr != null) { + String signature = attr.getSignature(originalIndex, visibleOffset); + if (signature != null) { + GenericFieldDescriptor descriptor = GenericMain.parseFieldSignature(signature); + if (descriptor != null) { + buffer.append(GenericMain.getGenericCastTypeName(descriptor.type)); + return; + } + } + } + } + + // then try from descriptor + StructLocalVariableTableAttribute attr = method.methodStruct.getLocalVariableAttr(); + if (attr != null) { + String descriptor = attr.getDescriptor(originalIndex, visibleOffset); + if (descriptor != null) { + buffer.append(ExprProcessor.getCastTypeName(new VarType(descriptor))); + return; + } + } + } + } + } + + buffer.append(ExprProcessor.getCastTypeName(getVarType())); + } + + @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof VarExprent)) return false; + if (!(o instanceof VarExprent)) return false; VarExprent ve = (VarExprent)o; return index == ve.getIndex() && version == ve.getVersion() && - InterpreterUtil.equalObjects(getVartype(), ve.getVartype()); // FIXME: vartype comparison redundant? + InterpreterUtil.equalObjects(getVarType(), ve.getVarType()); // FIXME: varType comparison redundant? } public int getIndex() { @@ -128,30 +186,26 @@ public class VarExprent extends Exprent implements SortUtil.Indexed { // Spigot public void setIndex(int index) { this.index = index; + if (processor != null) { + name = processor.getVarName(new VarVersionPair(this.index, this.version)); + } } - // Spigot Start - @Override - public int getSortIndex() { - return (definition) ? index : -1; - } - // Spigot End - - public VarType getVartype() { + public VarType getVarType() { VarType vt = null; if (processor != null) { - vt = processor.getVarType(new VarVersionPaar(index, version)); + vt = processor.getVarType(getVarVersionPair()); } - if (vt == null || (vartype != null && vartype.type != CodeConstants.TYPE_UNKNOWN)) { - vt = vartype; + if (vt == null || (varType != null && varType.type != CodeConstants.TYPE_UNKNOWN)) { + vt = varType; } return vt == null ? VarType.VARTYPE_UNKNOWN : vt; } - public void setVartype(VarType vartype) { - this.vartype = vartype; + public void setVarType(VarType varType) { + this.varType = varType; } public boolean isDefinition() { @@ -166,10 +220,6 @@ public class VarExprent extends Exprent implements SortUtil.Indexed { // Spigot return processor; } - public void setProcessor(VarProcessor processor) { - this.processor = processor; - } - public int getVersion() { return version; } @@ -178,12 +228,12 @@ public class VarExprent extends Exprent implements SortUtil.Indexed { // Spigot this.version = version; } - public boolean isClassdef() { - return classdef; + public boolean isClassDef() { + return classDef; } - public void setClassdef(boolean classdef) { - this.classdef = classdef; + public void setClassDef(boolean classDef) { + this.classDef = classDef; } public boolean isStack() { @@ -193,4 +243,27 @@ public class VarExprent extends Exprent implements SortUtil.Indexed { // Spigot public void setStack(boolean stack) { this.stack = stack; } -} + + // ***************************************************************************** + // IMatchable implementation + // ***************************************************************************** + + @Override + public boolean match(MatchNode matchNode, MatchEngine engine) { + if (!super.match(matchNode, engine)) { + return false; + } + + RuleValue rule = matchNode.getRules().get(MatchProperties.EXPRENT_VAR_INDEX); + if (rule != null) { + if (rule.isVariable()) { + return engine.checkAndSetVariableValue((String)rule.value, this.index); + } + else { + return this.index == Integer.valueOf((String)rule.value); + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java index b24b72c..aa2fae8 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.sforms; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; @@ -27,24 +13,24 @@ import java.util.List; public class DirectGraph { - public VBStyleCollection nodes = new VBStyleCollection(); + public final VBStyleCollection nodes = new VBStyleCollection<>(); public DirectNode first; // exit, [source, destination] - public HashMap> mapShortRangeFinallyPaths = new HashMap>(); + public final HashMap> mapShortRangeFinallyPaths = new HashMap<>(); // exit, [source, destination] - public HashMap> mapLongRangeFinallyPaths = new HashMap>(); + public final HashMap> mapLongRangeFinallyPaths = new HashMap<>(); // negative if branches (recorded for handling of && and ||) - public HashMap mapNegIfBranch = new HashMap(); + public final HashMap mapNegIfBranch = new HashMap<>(); // nodes, that are exception exits of a finally block with monitor variable - public HashMap mapFinallyMonitorExceptionPathExits = new HashMap(); + public final HashMap mapFinallyMonitorExceptionPathExits = new HashMap<>(); public void sortReversePostOrder() { - LinkedList res = new LinkedList(); + LinkedList res = new LinkedList<>(); addToReversePostOrderListIterative(first, res); nodes.clear(); @@ -53,12 +39,12 @@ public class DirectGraph { } } - private static void addToReversePostOrderListIterative(DirectNode root, List lst) { + private static void addToReversePostOrderListIterative(DirectNode root, List lst) { - LinkedList stackNode = new LinkedList(); - LinkedList stackIndex = new LinkedList(); + LinkedList stackNode = new LinkedList<>(); + LinkedList stackIndex = new LinkedList<>(); - HashSet setVisited = new HashSet(); + HashSet setVisited = new HashSet<>(); stackNode.add(root); stackIndex.add(0); @@ -94,10 +80,10 @@ public class DirectGraph { public boolean iterateExprents(ExprentIterator iter) { - LinkedList stack = new LinkedList(); + LinkedList stack = new LinkedList<>(); stack.add(first); - HashSet setVisited = new HashSet(); + HashSet setVisited = new HashSet<>(); while (!stack.isEmpty()) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java index 73303ec..c48d14a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.sforms; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; @@ -32,19 +18,19 @@ public class DirectNode { public static final int NODE_INCREMENT = 5; public static final int NODE_TRY = 6; - public int type; + public final int type; - public String id; + public final String id; public BasicBlockStatement block; - public Statement statement; + public final Statement statement; - public List exprents = new ArrayList(); + public List exprents = new ArrayList<>(); - public List succs = new ArrayList(); + public final List succs = new ArrayList<>(); - public List preds = new ArrayList(); + public final List preds = new ArrayList<>(); public DirectNode(int type, Statement statement, String id) { this.type = type; 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 05d2f33..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,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -26,19 +12,19 @@ import java.util.Map.Entry; public class FlattenStatementsHelper { // statement.id, node.id(direct), node.id(continue) - private Map mapDestinationNodes = new HashMap(); + private final Map mapDestinationNodes = new HashMap<>(); // node.id(source), statement.id(destination), edge type - private List listEdges = new ArrayList(); + private final List listEdges = new ArrayList<>(); // node.id(exit), [node.id(source), statement.id(destination)] - private Map> mapShortRangeFinallyPathIds = new HashMap>(); + private final Map> mapShortRangeFinallyPathIds = new HashMap<>(); // node.id(exit), [node.id(source), statement.id(destination)] - private Map> mapLongRangeFinallyPathIds = new HashMap>(); + private final Map> mapLongRangeFinallyPathIds = new HashMap<>(); // positive if branches - private Map mapPosIfBranch = new HashMap(); + private final Map mapPosIfBranch = new HashMap<>(); private DirectGraph graph; @@ -55,7 +41,7 @@ public class FlattenStatementsHelper { // dummy exit node Statement dummyexit = root.getDummyExit(); DirectNode node = new DirectNode(DirectNode.NODE_DIRECT, dummyexit, dummyexit.id.toString()); - node.exprents = new ArrayList(); + node.exprents = new ArrayList<>(); graph.nodes.addWithKey(node, node.id); mapDestinationNodes.put(dummyexit.id, new String[]{node.id, null}); @@ -70,24 +56,24 @@ public class FlattenStatementsHelper { private void flattenStatement() { class StatementStackEntry { - public Statement statement; - public LinkedList stackFinally; - public List tailExprents; + public final Statement statement; + public final LinkedList stackFinally; + public final List tailExprents; public int statementIndex; public int edgeIndex; public List succEdges; - public StatementStackEntry(Statement statement, LinkedList stackFinally, List tailExprents) { + StatementStackEntry(Statement statement, LinkedList stackFinally, List tailExprents) { this.statement = statement; this.stackFinally = stackFinally; this.tailExprents = tailExprents; } } - LinkedList lstStackStatements = new LinkedList(); + LinkedList lstStackStatements = new LinkedList<>(); - lstStackStatements.add(new StatementStackEntry(root, new LinkedList(), null)); + lstStackStatements.add(new StatementStackEntry(root, new LinkedList<>(), null)); mainloop: while (!lstStackStatements.isEmpty()) { @@ -100,7 +86,7 @@ public class FlattenStatementsHelper { DirectNode node, nd; - List lstSuccEdges = new ArrayList(); + List lstSuccEdges = new ArrayList<>(); DirectNode sourcenode = null; if (statEntry.succEdges == null) { @@ -143,14 +129,14 @@ public class FlattenStatementsHelper { mapDestinationNodes.put(stat.id, new String[]{firstnd.id, null}); graph.nodes.putWithKey(firstnd, firstnd.id); - LinkedList lst = new LinkedList(); + LinkedList lst = new LinkedList<>(); for (Statement st : stat.getStats()) { listEdges.add(new Edge(firstnd.id, st.id, StatEdge.TYPE_REGULAR)); LinkedList stack = stackFinally; if (stat.type == Statement.TYPE_CATCHALL && ((CatchAllStatement)stat).isFinally()) { - stack = new LinkedList(stackFinally); + stack = new LinkedList<>(stackFinally); if (st == stat.getFirst()) { // catch head stack.add(new StackEntry((CatchAllStatement)stat, Boolean.FALSE)); @@ -306,7 +292,7 @@ public class FlattenStatementsHelper { StatEdge edge = lstSuccEdges.get(edgeindex); - LinkedList stack = new LinkedList(stackFinally); + LinkedList stack = new LinkedList<>(stackFinally); int edgetype = edge.getType(); Statement destination = edge.getDestination(); @@ -412,21 +398,19 @@ public class FlattenStatementsHelper { } if (finallyShortRangeSource != null) { - boolean isContinueEdge = (edgetype == StatEdge.TYPE_CONTINUE); - List lst = mapShortRangeFinallyPathIds.get(sourcenode.id); - if (lst == null) { - mapShortRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList()); - } - lst.add(new String[]{finallyShortRangeSource.id, destination.id.toString(), finallyShortRangeEntry.id.toString(), - isFinallyMonitorExceptionPath ? "1" : null, isContinueEdge ? "1" : null}); + mapShortRangeFinallyPathIds.computeIfAbsent(sourcenode.id, k -> new ArrayList<>()).add(new String[]{ + finallyShortRangeSource.id, + destination.id.toString(), + finallyShortRangeEntry.id.toString(), + isFinallyMonitorExceptionPath ? "1" : null, + isContinueEdge ? "1" : null}); - lst = mapLongRangeFinallyPathIds.get(sourcenode.id); - if (lst == null) { - mapLongRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList()); - } - lst.add(new String[]{finallyLongRangeSource.id, destination.id.toString(), finallyLongRangeEntry.id.toString(), + mapLongRangeFinallyPathIds.computeIfAbsent(sourcenode.id, k -> new ArrayList<>()).add(new String[]{ + finallyLongRangeSource.id, + destination.id.toString(), + finallyLongRangeEntry.id.toString(), isContinueEdge ? "1" : null}); } } @@ -458,7 +442,7 @@ public class FlattenStatementsHelper { for (int i = 0; i < 2; i++) { for (Entry> ent : (i == 0 ? mapShortRangeFinallyPathIds : mapLongRangeFinallyPathIds).entrySet()) { - List newLst = new ArrayList(); + List newLst = new ArrayList<>(); List lst = ent.getValue(); for (String[] arr : lst) { @@ -477,8 +461,8 @@ public class FlattenStatementsHelper { if (!newLst.isEmpty()) { (i == 0 ? graph.mapShortRangeFinallyPaths : graph.mapLongRangeFinallyPaths).put(ent.getKey(), - new ArrayList( - new HashSet(newLst))); + new ArrayList<>( + new HashSet<>(newLst))); } } } @@ -488,10 +472,10 @@ public class FlattenStatementsHelper { return mapDestinationNodes; } - public static class FinallyPathWrapper { - public String source; - public String destination; - public String entry; + public static final class FinallyPathWrapper { + public final String source; + public final String destination; + public final String entry; private FinallyPathWrapper(String source, String destination, String entry) { this.source = source; @@ -502,7 +486,7 @@ public class FlattenStatementsHelper { @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof FinallyPathWrapper)) return false; + if (!(o instanceof FinallyPathWrapper)) return false; FinallyPathWrapper fpw = (FinallyPathWrapper)o; return (source + ":" + destination + ":" + entry).equals(fpw.source + ":" + fpw.destination + ":" + fpw.entry); @@ -522,18 +506,18 @@ public class FlattenStatementsHelper { private static class StackEntry { - public CatchAllStatement catchstatement; - public boolean state; - public int edgetype; - public boolean isFinallyExceptionPath; + public final CatchAllStatement catchstatement; + public final boolean state; + public final int edgetype; + public final boolean isFinallyExceptionPath; - public Statement destination; - public Statement finallyShortRangeEntry; - public Statement finallyLongRangeEntry; - public DirectNode finallyShortRangeSource; - public DirectNode finallyLongRangeSource; + public final Statement destination; + public final Statement finallyShortRangeEntry; + public final Statement finallyLongRangeEntry; + public final DirectNode finallyShortRangeSource; + public final DirectNode finallyLongRangeSource; - public StackEntry(CatchAllStatement catchstatement, + StackEntry(CatchAllStatement catchstatement, boolean state, int edgetype, Statement destination, @@ -555,17 +539,17 @@ public class FlattenStatementsHelper { this.finallyLongRangeSource = finallyLongRangeSource; } - public StackEntry(CatchAllStatement catchstatement, boolean state) { + StackEntry(CatchAllStatement catchstatement, boolean state) { this(catchstatement, state, -1, null, null, null, null, null, false); } } private static class Edge { - public String sourceid; - public Integer statid; - public int edgetype; + public final String sourceid; + public final Integer statid; + public final int edgetype; - public Edge(String sourceid, Integer statid, int edgetype) { + Edge(String sourceid, Integer statid, int edgetype) { this.sourceid = sourceid; this.statid = statid; this.edgetype = edgetype; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java index dbec793..29e7030 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.sforms; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -25,7 +11,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; 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.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.util.FastSparseSetFactory; @@ -33,7 +19,6 @@ import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.SFormsFastMapDirect; -import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -42,24 +27,22 @@ import java.util.Map.Entry; public class SSAConstructorSparseEx { // node id, var, version - private HashMap inVarVersions = new HashMap(); + private final HashMap inVarVersions = new HashMap<>(); // node id, var, version (direct branch) - private HashMap outVarVersions = new HashMap(); + private final HashMap outVarVersions = new HashMap<>(); // node id, var, version (negative branch) - private HashMap outNegVarVersions = new HashMap(); + private final HashMap outNegVarVersions = new HashMap<>(); // node id, var, version - private HashMap extraVarVersions = new HashMap(); + private final HashMap extraVarVersions = new HashMap<>(); // (var, version), version - private HashMap> phi = new HashMap>(); + private final HashMap> phi = new HashMap<>(); // var, version - private HashMap lastversion = new HashMap(); - - private List startVars = new ArrayList(); + private final HashMap lastversion = new HashMap<>(); // set factory private FastSparseSetFactory factory; @@ -73,18 +56,18 @@ public class SSAConstructorSparseEx { // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot")); // } catch(Exception ex) {ex.printStackTrace();} - HashSet setInit = new HashSet(); + HashSet setInit = new HashSet<>(); for (int i = 0; i < 64; i++) { setInit.add(i); } - factory = new FastSparseSetFactory(setInit); + factory = new FastSparseSetFactory<>(setInit); SFormsFastMapDirect firstmap = createFirstMap(mt); extraVarVersions.put(dgraph.first.id, firstmap); setCatchMaps(root, dgraph, flatthelper); - HashSet updated = new HashSet(); + HashSet updated = new HashSet<>(); do { // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); ssaStatements(dgraph, updated); @@ -157,7 +140,7 @@ public class SSAConstructorSparseEx { switch (expr.type) { case Exprent.EXPRENT_ASSIGNMENT: AssignmentExprent assexpr = (AssignmentExprent)expr; - if (assexpr.getCondtype() == AssignmentExprent.CONDITION_NONE) { + if (assexpr.getCondType() == AssignmentExprent.CONDITION_NONE) { Exprent dest = assexpr.getLeft(); if (dest.type == Exprent.EXPRENT_VAR) { varassign = (VarExprent)dest; @@ -166,7 +149,7 @@ public class SSAConstructorSparseEx { break; case Exprent.EXPRENT_FUNCTION: FunctionExprent func = (FunctionExprent)expr; - switch (func.getFunctype()) { + switch (func.getFuncType()) { case FunctionExprent.FUNCTION_IIF: processExprent(func.getLstOperands().get(0), varmaparr); @@ -260,12 +243,12 @@ public class SSAConstructorSparseEx { if (cardinality == 1) { // == 1 // set version Integer it = vers.iterator().next(); - vardest.setVersion(it.intValue()); + vardest.setVersion(it); } else if (cardinality == 2) { // size > 1 Integer current_vers = vardest.getVersion(); - VarVersionPaar currpaar = new VarVersionPaar(varindex, current_vers); + VarVersionPair currpaar = new VarVersionPair(varindex, current_vers); if (current_vers != 0 && phi.containsKey(currpaar)) { setCurrentVar(varmap, varindex, current_vers); // update phi node @@ -279,7 +262,7 @@ public class SSAConstructorSparseEx { setCurrentVar(varmap, varindex, nextver); // create new phi node - phi.put(new VarVersionPaar(varindex, nextver), vers); + phi.put(new VarVersionPair(varindex, nextver), vers); } } // 0 means uninitialized variable, which is impossible } @@ -288,10 +271,10 @@ public class SSAConstructorSparseEx { private Integer getNextFreeVersion(Integer var) { Integer nextver = lastversion.get(var); if (nextver == null) { - nextver = new Integer(1); + nextver = 1; } else { - nextver = new Integer(nextver.intValue() + 1); + nextver++; } lastversion.put(var, nextver); return nextver; @@ -348,7 +331,7 @@ public class SSAConstructorSparseEx { String exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.get(predid); boolean isExceptionMonitorExit = (exceptionDest != null && !nodeid.equals(exceptionDest)); - HashSet setLongPathWrapper = new HashSet(); + HashSet setLongPathWrapper = new HashSet<>(); for (FinallyPathWrapper finwraplong : dgraph.mapLongRangeFinallyPaths.get(predid)) { setLongPathWrapper.add(finwraplong.destination + "##" + finwraplong.source); } @@ -372,7 +355,7 @@ public class SSAConstructorSparseEx { } // false path? - boolean isFalsePath = true; + boolean isFalsePath; if (recFinally) { isFalsePath = !finwrap.destination.equals(nodeid); @@ -477,7 +460,6 @@ public class SSAConstructorSparseEx { setCurrentVar(map, varindex, version); extraVarVersions.put(dgraph.nodes.getWithKey(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0]).id, map); - startVars.add(new VarVersionPaar(varindex, version)); } } @@ -501,29 +483,24 @@ public class SSAConstructorSparseEx { FastSparseSet set = factory.spawnEmptySet(); set.add(version); map.put(varindex, set); - startVars.add(new VarVersionPaar(varindex, version)); if (thisvar) { if (i == 0) { varindex++; } else { - varindex += md.params[i - 1].stack_size; + varindex += md.params[i - 1].stackSize; } } else { - varindex += md.params[i].stack_size; + varindex += md.params[i].stackSize; } } return map; } - public HashMap> getPhi() { + public HashMap> getPhi() { return phi; } - - public List getStartVars() { - return startVars; - } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java index fbd652c..4edec05 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.sforms; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -21,7 +7,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatements import org.jetbrains.java.decompiler.modules.decompiler.stats.*; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionEdge; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionNode; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsGraph; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; @@ -40,42 +26,39 @@ import java.util.Map.Entry; public class SSAUConstructorSparseEx { // node id, var, version - private HashMap inVarVersions = new HashMap(); + private final HashMap inVarVersions = new HashMap<>(); //private HashMap>> inVarVersions = new HashMap>>(); // node id, var, version (direct branch) - private HashMap outVarVersions = new HashMap(); + private final HashMap outVarVersions = new HashMap<>(); //private HashMap>> outVarVersions = new HashMap>>(); // node id, var, version (negative branch) - private HashMap outNegVarVersions = new HashMap(); + private final HashMap outNegVarVersions = new HashMap<>(); //private HashMap>> outNegVarVersions = new HashMap>>(); // node id, var, version - private HashMap extraVarVersions = new HashMap(); + private final HashMap extraVarVersions = new HashMap<>(); //private HashMap>> extraVarVersions = new HashMap>>(); - // (var, version), version - private HashMap> phi = new HashMap>(); - // var, version - private HashMap lastversion = new HashMap(); + private final HashMap lastversion = new HashMap<>(); // version, protected ranges (catch, finally) - private HashMap mapVersionFirstRange = new HashMap(); + private final HashMap mapVersionFirstRange = new HashMap<>(); // version, version - private HashMap phantomppnodes = new HashMap(); // ++ and -- + private final HashMap phantomppnodes = new HashMap<>(); // ++ and -- // node.id, version, version - private HashMap> phantomexitnodes = - new HashMap>(); // finally exits + private final HashMap> phantomexitnodes = + new HashMap<>(); // finally exits // versions memory dependencies - private VarVersionsGraph ssuversions = new VarVersionsGraph(); + private final VarVersionsGraph ssuversions = new VarVersionsGraph(); // field access vars (exprent id, var id) - private HashMap mapFieldVars = new HashMap(); + private final HashMap mapFieldVars = new HashMap<>(); // field access counter private int fieldvarcounter = -1; @@ -88,11 +71,11 @@ public class SSAUConstructorSparseEx { FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); DirectGraph dgraph = flatthelper.buildDirectGraph(root); - HashSet setInit = new HashSet(); + HashSet setInit = new HashSet<>(); for (int i = 0; i < 64; i++) { setInit.add(i); } - factory = new FastSparseSetFactory(setInit); + factory = new FastSparseSetFactory<>(setInit); extraVarVersions.put(dgraph.first.id, createFirstMap(mt, root)); @@ -102,7 +85,7 @@ public class SSAUConstructorSparseEx { // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot")); // } catch(Exception ex) {ex.printStackTrace();} - HashSet updated = new HashSet(); + HashSet updated = new HashSet<>(); do { // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); ssaStatements(dgraph, updated, false); @@ -177,7 +160,7 @@ public class SSAUConstructorSparseEx { switch (expr.type) { case Exprent.EXPRENT_ASSIGNMENT: AssignmentExprent assexpr = (AssignmentExprent)expr; - if (assexpr.getCondtype() == AssignmentExprent.CONDITION_NONE) { + if (assexpr.getCondType() == AssignmentExprent.CONDITION_NONE) { Exprent dest = assexpr.getLeft(); if (dest.type == Exprent.EXPRENT_VAR) { varassign = (VarExprent)dest; @@ -186,7 +169,7 @@ public class SSAUConstructorSparseEx { break; case Exprent.EXPRENT_FUNCTION: FunctionExprent func = (FunctionExprent)expr; - switch (func.getFunctype()) { + switch (func.getFuncType()) { case FunctionExprent.FUNCTION_IIF: processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars); @@ -264,14 +247,14 @@ public class SSAUConstructorSparseEx { mapFieldVars.put(expr.id, index); // ssu graph - ssuversions.createNode(new VarVersionPaar(index, 1)); + ssuversions.createNode(new VarVersionPair(index, 1)); } setCurrentVar(varmap, index, 1); } else if (expr.type == Exprent.EXPRENT_INVOCATION || (expr.type == Exprent.EXPRENT_ASSIGNMENT && ((AssignmentExprent)expr).getLeft().type == Exprent.EXPRENT_FIELD) || - (expr.type == Exprent.EXPRENT_NEW && ((NewExprent)expr).getNewtype().type == CodeConstants.TYPE_OBJECT) || + (expr.type == Exprent.EXPRENT_NEW && ((NewExprent)expr).getNewType().type == CodeConstants.TYPE_OBJECT) || expr.type == Exprent.EXPRENT_FUNCTION) { boolean ismmpp = true; @@ -281,7 +264,7 @@ public class SSAUConstructorSparseEx { ismmpp = false; FunctionExprent fexpr = (FunctionExprent)expr; - if (fexpr.getFunctype() >= FunctionExprent.FUNCTION_IMM && fexpr.getFunctype() <= FunctionExprent.FUNCTION_PPI) { + if (fexpr.getFuncType() >= FunctionExprent.FUNCTION_IMM && fexpr.getFuncType() <= FunctionExprent.FUNCTION_PPI) { if (fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { ismmpp = true; } @@ -306,13 +289,13 @@ public class SSAUConstructorSparseEx { varassign.setVersion(nextver); // ssu graph - ssuversions.createNode(new VarVersionPaar(varindex, nextver)); + ssuversions.createNode(new VarVersionPair(varindex, nextver)); setCurrentVar(varmap, varindex, nextver); } else { if (calcLiveVars) { - varMapToGraph(new VarVersionPaar(varindex.intValue(), varassign.getVersion()), varmap); + varMapToGraph(new VarVersionPair(varindex.intValue(), varassign.getVersion()), varmap); } setCurrentVar(varmap, varindex, varassign.getVersion()); } @@ -320,7 +303,7 @@ public class SSAUConstructorSparseEx { else if (expr.type == Exprent.EXPRENT_FUNCTION) { // MM or PP function FunctionExprent func = (FunctionExprent)expr; - switch (func.getFunctype()) { + switch (func.getFuncType()) { case FunctionExprent.FUNCTION_IMM: case FunctionExprent.FUNCTION_MMI: case FunctionExprent.FUNCTION_IPP: @@ -329,14 +312,14 @@ public class SSAUConstructorSparseEx { if (func.getLstOperands().get(0).type == Exprent.EXPRENT_VAR) { VarExprent var = (VarExprent)func.getLstOperands().get(0); Integer varindex = var.getIndex(); - VarVersionPaar varpaar = new VarVersionPaar(varindex.intValue(), var.getVersion()); + VarVersionPair varpaar = new VarVersionPair(varindex.intValue(), var.getVersion()); // ssu graph - VarVersionPaar phantomver = phantomppnodes.get(varpaar); + VarVersionPair phantomver = phantomppnodes.get(varpaar); if (phantomver == null) { // get next version Integer nextver = getNextFreeVersion(varindex, null); - phantomver = new VarVersionPaar(varindex, nextver); + phantomver = new VarVersionPair(varindex, nextver); //ssuversions.createOrGetNode(phantomver); ssuversions.createNode(phantomver); @@ -358,7 +341,7 @@ public class SSAUConstructorSparseEx { if (calcLiveVars) { varMapToGraph(varpaar, varmap); } - setCurrentVar(varmap, varindex.intValue(), var.getVersion()); + setCurrentVar(varmap, varindex, var.getVersion()); } } } @@ -373,9 +356,9 @@ public class SSAUConstructorSparseEx { int cardinality = vers.getCardinality(); if (cardinality == 1) { // size == 1 - if (current_vers.intValue() != 0) { + if (current_vers != 0) { if (calcLiveVars) { - varMapToGraph(new VarVersionPaar(varindex, current_vers), varmap); + varMapToGraph(new VarVersionPair(varindex, current_vers), varmap); } setCurrentVar(varmap, varindex, current_vers); } @@ -389,8 +372,8 @@ public class SSAUConstructorSparseEx { // ssu graph Integer lastver = vers.iterator().next(); - VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPaar(varindex, lastver)); - VarVersionNode usenode = ssuversions.createNode(new VarVersionPaar(varindex, usever)); + VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPair(varindex, lastver)); + VarVersionNode usenode = ssuversions.createNode(new VarVersionPair(varindex, usever)); VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, prenode, usenode); prenode.addSuccessor(edge); usenode.addPredecessor(edge); @@ -398,9 +381,9 @@ public class SSAUConstructorSparseEx { } else if (cardinality == 2) { // size > 1 - if (current_vers.intValue() != 0) { + if (current_vers != 0) { if (calcLiveVars) { - varMapToGraph(new VarVersionPaar(varindex, current_vers), varmap); + varMapToGraph(new VarVersionPair(varindex, current_vers), varmap); } setCurrentVar(varmap, varindex, current_vers); } @@ -411,29 +394,29 @@ public class SSAUConstructorSparseEx { vardest.setVersion(usever); // ssu node - ssuversions.createNode(new VarVersionPaar(varindex, usever)); + ssuversions.createNode(new VarVersionPair(varindex, usever)); setCurrentVar(varmap, varindex, usever); current_vers = usever; } - createOrUpdatePhiNode(new VarVersionPaar(varindex, current_vers), vers, stat); + createOrUpdatePhiNode(new VarVersionPair(varindex, current_vers), vers, stat); } // vers.size() == 0 means uninitialized variable, which is impossible } } - private void createOrUpdatePhiNode(VarVersionPaar phivar, FastSparseSet vers, Statement stat) { + private void createOrUpdatePhiNode(VarVersionPair phivar, FastSparseSet vers, Statement stat) { FastSparseSet versCopy = vers.getCopy(); - HashSet phiVers = new HashSet(); + HashSet phiVers = new HashSet<>(); // take into account the corresponding mm/pp node if existing int ppvers = phantomppnodes.containsKey(phivar) ? phantomppnodes.get(phivar).version : -1; // ssu graph VarVersionNode phinode = ssuversions.nodes.getWithKey(phivar); - List lstPreds = new ArrayList(phinode.preds); + List lstPreds = new ArrayList<>(phinode.preds); if (lstPreds.size() == 1) { // not yet a phi node VarVersionEdge edge = lstPreds.get(0); @@ -454,19 +437,19 @@ public class SSAUConstructorSparseEx { } } - List colnodes = new ArrayList(); - List colpaars = new ArrayList(); + List colnodes = new ArrayList<>(); + List colpaars = new ArrayList<>(); for (Integer ver : versCopy) { - VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPaar(phivar.var, ver.intValue())); + VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPair(phivar.var, ver.intValue())); Integer tempver = getNextFreeVersion(phivar.var, stat); - VarVersionNode tempnode = new VarVersionNode(phivar.var, tempver.intValue()); + VarVersionNode tempnode = new VarVersionNode(phivar.var, tempver); colnodes.add(tempnode); - colpaars.add(new VarVersionPaar(phivar.var, tempver.intValue())); + colpaars.add(new VarVersionPair(phivar.var, tempver.intValue())); VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, prenode, tempnode); @@ -482,14 +465,11 @@ public class SSAUConstructorSparseEx { } ssuversions.addNodes(colnodes, colpaars); - - // update phi node - phi.put(phivar, phiVers); } - private void varMapToGraph(VarVersionPaar varpaar, SFormsFastMapDirect varmap) { + private void varMapToGraph(VarVersionPair varpaar, SFormsFastMapDirect varmap) { - VBStyleCollection nodes = ssuversions.nodes; + VBStyleCollection nodes = ssuversions.nodes; VarVersionNode node = nodes.getWithKey(varpaar); @@ -501,10 +481,10 @@ public class SSAUConstructorSparseEx { Integer nextver = lastversion.get(var); if (nextver == null) { - nextver = new Integer(1); + nextver = 1; } else { - nextver = new Integer(nextver.intValue() + 1); + nextver++; } lastversion.put(var, nextver); @@ -512,7 +492,7 @@ public class SSAUConstructorSparseEx { if (stat != null) { // null iff phantom version Integer firstRangeId = getFirstProtectedRange(stat); if (firstRangeId != null) { - mapVersionFirstRange.put(new VarVersionPaar(var, nextver), firstRangeId); + mapVersionFirstRange.put(new VarVersionPair(var, nextver), firstRangeId); } } @@ -571,7 +551,7 @@ public class SSAUConstructorSparseEx { String exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.get(predid); boolean isExceptionMonitorExit = (exceptionDest != null && !nodeid.equals(exceptionDest)); - HashSet setLongPathWrapper = new HashSet(); + HashSet setLongPathWrapper = new HashSet<>(); for (List lstwrapper : dgraph.mapLongRangeFinallyPaths.values()) { for (FinallyPathWrapper finwraplong : lstwrapper) { setLongPathWrapper.add(finwraplong.destination + "##" + finwraplong.source); @@ -597,7 +577,7 @@ public class SSAUConstructorSparseEx { } // false path? - boolean isFalsePath = true; + boolean isFalsePath; if (recFinally) { isFalsePath = !finwrap.destination.equals(nodeid); @@ -633,9 +613,9 @@ public class SSAUConstructorSparseEx { if (!mapTrueSource.isEmpty() && !mapNew.isEmpty()) { // FIXME: what for?? // replace phi versions with corresponding phantom ones - HashMap mapPhantom = phantomexitnodes.get(predid); + HashMap mapPhantom = phantomexitnodes.get(predid); if (mapPhantom == null) { - mapPhantom = new HashMap(); + mapPhantom = new HashMap<>(); } SFormsFastMapDirect mapExitVar = mapNew.getCopy(); @@ -645,17 +625,17 @@ public class SSAUConstructorSparseEx { for (Integer version : ent.getValue()) { Integer varindex = ent.getKey(); - VarVersionPaar exitvar = new VarVersionPaar(varindex, version); + VarVersionPair exitvar = new VarVersionPair(varindex, version); FastSparseSet newSet = mapNew.get(varindex); // remove the actual exit version newSet.remove(version); // get or create phantom version - VarVersionPaar phantomvar = mapPhantom.get(exitvar); + VarVersionPair phantomvar = mapPhantom.get(exitvar); if (phantomvar == null) { Integer newversion = getNextFreeVersion(exitvar.var, null); - phantomvar = new VarVersionPaar(exitvar.var, newversion.intValue()); + phantomvar = new VarVersionPair(exitvar.var, newversion.intValue()); VarVersionNode exitnode = ssuversions.nodes.getWithKey(exitvar); VarVersionNode phantomnode = ssuversions.createNode(phantomvar); @@ -745,8 +725,8 @@ public class SSAUConstructorSparseEx { setCurrentVar(map, varindex, version); extraVarVersions.put(dgraph.nodes.getWithKey(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0]).id, map); - //ssuversions.createOrGetNode(new VarVersionPaar(varindex, version)); - ssuversions.createNode(new VarVersionPaar(varindex, version)); + //ssuversions.createOrGetNode(new VarVersionPair(varindex, version)); + ssuversions.createNode(new VarVersionPair(varindex, version)); } } @@ -770,18 +750,18 @@ public class SSAUConstructorSparseEx { FastSparseSet set = factory.spawnEmptySet(); set.add(version); map.put(varindex, set); - ssuversions.createNode(new VarVersionPaar(varindex, version)); + ssuversions.createNode(new VarVersionPair(varindex, version)); if (thisvar) { if (i == 0) { varindex++; } else { - varindex += md.params[i - 1].stack_size; + varindex += md.params[i - 1].stackSize; } } else { - varindex += md.params[i].stack_size; + varindex += md.params[i].stackSize; } } @@ -815,15 +795,11 @@ public class SSAUConstructorSparseEx { return null; } - public HashMap> getPhi() { - return phi; - } - public VarVersionsGraph getSsuversions() { return ssuversions; } - public SFormsFastMapDirect getLiveVarVersionsMap(VarVersionPaar varpaar) { + public SFormsFastMapDirect getLiveVarVersionsMap(VarVersionPair varpaar) { VarVersionNode node = ssuversions.nodes.getWithKey(varpaar); @@ -834,7 +810,7 @@ public class SSAUConstructorSparseEx { return null; } - public HashMap getMapVersionFirstRange() { + public HashMap getMapVersionFirstRange() { return mapVersionFirstRange; } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java index 2fb809e..262a01f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.stats; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -23,7 +9,7 @@ import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; -import org.jetbrains.java.decompiler.util.Util; +import org.jetbrains.java.decompiler.util.TextBuffer; public class BasicBlockStatement extends Statement { @@ -31,7 +17,7 @@ public class BasicBlockStatement extends Statement { // private fields // ***************************************************************************** - private BasicBlock block; + private final BasicBlock block; // ***************************************************************************** // constructors @@ -67,11 +53,14 @@ public class BasicBlockStatement extends Statement { // public methods // ***************************************************************************** - public String toJava(int indent, BytecodeMappingTracer tracer) { - return ExprProcessor.listToJava(varDefinitions, indent, tracer) + - ExprProcessor.listToJava(exprents, indent, tracer); + @Override + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + TextBuffer tb = ExprProcessor.listToJava(varDefinitions, indent, tracer); + tb.append(ExprProcessor.listToJava(exprents, indent, tracer)); + return tb; } + @Override public Statement getSimpleCopy() { BasicBlock newblock = new BasicBlock( 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 b993a81..2fa2d90 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -23,16 +9,15 @@ import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.struct.gen.VarType; -import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.List; +import java.util.Set; -public class CatchAllStatement extends Statement { +public final class CatchAllStatement extends Statement { private Statement handler; @@ -40,7 +25,7 @@ public class CatchAllStatement extends Statement { private VarExprent monitor; - private List vars = new ArrayList(); + private final List vars = new ArrayList<>(); // ***************************************************************************** // constructors @@ -70,7 +55,7 @@ public class CatchAllStatement extends Statement { vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Throwable"), - (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); + DecompilerContext.getVarProcessor())); } @@ -79,13 +64,11 @@ public class CatchAllStatement extends Statement { // ***************************************************************************** public static Statement isHead(Statement head) { - if (head.getLastBasicType() != Statement.LASTBASICTYPE_GENERAL) { return null; } - HashSet setHandlers = DecHelper.getUniquePredExceptions(head); - + Set setHandlers = DecHelper.getUniquePredExceptions(head); if (setHandlers.size() != 1) { return null; } @@ -93,7 +76,7 @@ public class CatchAllStatement extends Statement { for (StatEdge edge : head.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) { Statement exc = edge.getDestination(); - if (edge.getExceptions() == null && setHandlers.contains(exc) && exc.getLastBasicType() == LASTBASICTYPE_GENERAL) { + if (edge.getExceptions() == null && exc.getLastBasicType() == LASTBASICTYPE_GENERAL && setHandlers.contains(exc)) { List lstSuccs = exc.getSuccessorEdges(STATEDGE_DIRECT_ALL); if (lstSuccs.isEmpty() || lstSuccs.get(0).getType() != StatEdge.TYPE_REGULAR) { @@ -111,64 +94,58 @@ public class CatchAllStatement extends Statement { return null; } - public String toJava(int indent, BytecodeMappingTracer tracer) { - String indstr = InterpreterUtil.getIndentString(indent); - String indstr1 = null; - + @Override + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { String new_line_separator = DecompilerContext.getNewLineSeparator(); - StringBuilder buf = new StringBuilder(); + TextBuffer buf = new TextBuffer(); - String defs = ExprProcessor.listToJava(varDefinitions, indent, tracer); - buf.append(defs); - if (!defs.isEmpty()) { - buf.append(new_line_separator); - } + buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer)); boolean labeled = isLabeled(); if (labeled) { - buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator); + buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } List lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); if (first.type == TYPE_TRYCATCH && first.varDefinitions.isEmpty() && isFinally && !labeled && !first.isLabeled() && (lstSuccs.isEmpty() || !lstSuccs.get(0).explicit)) { - String content = ExprProcessor.jmpWrapper(first, indent, true, tracer); - content = content.substring(0, content.length() - new_line_separator.length()); - + TextBuffer content = ExprProcessor.jmpWrapper(first, indent, true, tracer); + content.setLength(content.length() - new_line_separator.length()); + tracer.incrementCurrentSourceLine(-1); buf.append(content); } else { - buf.append(indstr).append("try {").append(new_line_separator); + buf.appendIndent(indent).append("try {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true, tracer)); - buf.append(indstr).append("}"); + buf.appendIndent(indent).append("}"); } buf.append(isFinally ? " finally" : - " catch (" + vars.get(0).toJava(indent, tracer) + ")").append(" {").append(new_line_separator); + " catch (" + vars.get(0).toJava(indent, tracer) + ")").append(" {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); if (monitor != null) { - indstr1 = InterpreterUtil.getIndentString(indent + 1); - buf.append(indstr1).append("if(").append(monitor.toJava(indent, tracer)).append(") {").append(new_line_separator); + buf.appendIndent(indent+1).append("if (").append(monitor.toJava(indent, tracer)).append(") {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } buf.append(ExprProcessor.jmpWrapper(handler, indent + 1 + (monitor != null ? 1 : 0), true, tracer)); if (monitor != null) { - buf.append(indstr1).append("}").append(new_line_separator); + buf.appendIndent(indent + 1).append("}").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } - buf.append(indstr).append("}").append(new_line_separator); + buf.appendIndent(indent).append("}").appendLineSeparator(); tracer.incrementCurrentSourceLine(); - return buf.toString(); + return buf; } + @Override public void replaceStatement(Statement oldstat, Statement newstat) { if (handler == oldstat) { @@ -178,6 +155,7 @@ public class CatchAllStatement extends Statement { super.replaceStatement(oldstat, newstat); } + @Override public Statement getSimpleCopy() { CatchAllStatement cas = new CatchAllStatement(); @@ -187,19 +165,19 @@ public class CatchAllStatement extends Statement { if (this.monitor != null) { cas.monitor = new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), VarType.VARTYPE_INT, - (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR)); + DecompilerContext.getVarProcessor()); } if (!this.vars.isEmpty()) { - // FIXME: WTF??? vars?! - vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), + cas.vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Throwable"), - (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); + DecompilerContext.getVarProcessor())); } return cas; } + @Override public void initSimpleCopy() { first = stats.get(0); handler = stats.get(1); @@ -213,27 +191,18 @@ public class CatchAllStatement extends Statement { return handler; } - - public void setHandler(Statement handler) { - this.handler = handler; - } - - public boolean isFinally() { return isFinally; } - public void setFinally(boolean isFinally) { this.isFinally = isFinally; } - public VarExprent getMonitor() { return monitor; } - public void setMonitor(VarExprent monitor) { this.monitor = monitor; } @@ -241,4 +210,4 @@ public class CatchAllStatement extends Statement { public List getVars() { return vars; } -} +} \ No newline at end of file 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 685a60e..5168f05 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java @@ -1,21 +1,8 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; @@ -23,19 +10,16 @@ import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; -import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.struct.gen.VarType; -import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; +import java.util.Set; -public class CatchStatement extends Statement { - - private List> exctstrings = new ArrayList>(); - - private List vars = new ArrayList(); +public final class CatchStatement extends Statement { + private final List> exctstrings = new ArrayList<>(); + private final List vars = new ArrayList<>(); // ***************************************************************************** // constructors @@ -45,8 +29,7 @@ public class CatchStatement extends Statement { type = TYPE_TRYCATCH; } - private CatchStatement(Statement head, Statement next, HashSet setHandlers) { - + private CatchStatement(Statement head, Statement next, Set setHandlers) { this(); first = head; @@ -57,12 +40,12 @@ public class CatchStatement extends Statement { if (setHandlers.contains(stat)) { stats.addWithKey(stat, stat.id); - exctstrings.add(new ArrayList(edge.getExceptions())); + exctstrings.add(new ArrayList<>(edge.getExceptions())); vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), new VarType(CodeConstants.TYPE_OBJECT, 0, edge.getExceptions().get(0)), // FIXME: for now simply the first type. Should get the first common superclass when possible. - (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); + DecompilerContext.getVarProcessor())); } } @@ -76,15 +59,12 @@ public class CatchStatement extends Statement { // ***************************************************************************** public static Statement isHead(Statement head) { - if (head.getLastBasicType() != LASTBASICTYPE_GENERAL) { return null; } - HashSet setHandlers = DecHelper.getUniquePredExceptions(head); - + Set setHandlers = DecHelper.getUniquePredExceptions(head); if (!setHandlers.isEmpty()) { - int hnextcount = 0; // either no statements with connection to next, or more than 1 Statement next = null; @@ -132,7 +112,7 @@ public class CatchStatement extends Statement { } if (hnextcount != 1 && !setHandlers.isEmpty()) { - List lst = new ArrayList(); + List lst = new ArrayList<>(); lst.add(head); lst.addAll(setHandlers); @@ -150,33 +130,35 @@ public class CatchStatement extends Statement { return null; } - public String toJava(int indent, BytecodeMappingTracer tracer) { - String indstr = InterpreterUtil.getIndentString(indent); - StringBuilder buf = new StringBuilder(); + @Override + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + TextBuffer buf = new TextBuffer(); - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - String content = ExprProcessor.listToJava(varDefinitions, indent, tracer); - buf.append(content); - if (!content.isEmpty()) { - buf.append(new_line_separator); - } + buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer)); if (isLabeled()) { - buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator); + buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } - buf.append(indstr).append("try {").append(new_line_separator); + buf.appendIndent(indent).append("try {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true, tracer)); - buf.append(indstr).append("}"); + buf.appendIndent(indent).append("}"); for (int i = 1; i < stats.size(); i++) { - List exception_types = exctstrings.get(i - 1); + Statement stat = stats.get(i); + // map first instruction storing the exception to the catch statement + BasicBlock block = stat.getBasichead().getBlock(); + if (!block.getSeq().isEmpty() && block.getInstruction(0).opcode == CodeConstants.opc_astore) { + Integer offset = block.getOldOffset(0); + if (offset > -1) tracer.addMapping(offset); + } buf.append(" catch ("); + + List exception_types = exctstrings.get(i - 1); if (exception_types.size() > 1) { // multi-catch, Java 7 style for (int exc_index = 1; exc_index < exception_types.size(); ++exc_index) { VarType exc_type = new VarType(CodeConstants.TYPE_OBJECT, 0, exception_types.get(exc_index)); @@ -186,27 +168,26 @@ public class CatchStatement extends Statement { } } buf.append(vars.get(i - 1).toJava(indent, tracer)); - buf.append(") {").append(new_line_separator); + buf.append(") {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); - buf.append(ExprProcessor.jmpWrapper(stats.get(i), indent + 1, true, tracer)).append(indstr) + buf.append(ExprProcessor.jmpWrapper(stat, indent + 1, false, tracer)).appendIndent(indent) .append("}"); - tracer.incrementCurrentSourceLine(); } - buf.append(new_line_separator); + buf.appendLineSeparator(); tracer.incrementCurrentSourceLine(); - return buf.toString(); + return buf; } + @Override public Statement getSimpleCopy() { - CatchStatement cs = new CatchStatement(); for (List exc : this.exctstrings) { - cs.exctstrings.add(new ArrayList(exc)); + cs.exctstrings.add(new ArrayList<>(exc)); cs.vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), new VarType(CodeConstants.TYPE_OBJECT, 0, exc.get(0)), - (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); + DecompilerContext.getVarProcessor())); } return cs; @@ -219,4 +200,4 @@ public class CatchStatement extends Statement { public List getVars() { return vars; } -} +} \ No newline at end of file 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 81f5ca2..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,33 +1,17 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.util.InterpreterUtil; -import org.jetbrains.java.decompiler.util.Util; +import org.jetbrains.java.decompiler.util.TextBuffer; 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; @@ -36,9 +20,9 @@ public class DoStatement extends Statement { private int looptype; - private List initExprent = new ArrayList(); - private List conditionExprent = new ArrayList(); - private List incExprent = new ArrayList(); + private final List initExprent = new ArrayList<>(); + private final List conditionExprent = new ArrayList<>(); + private final List incExprent = new ArrayList<>(); // ***************************************************************************** // constructors @@ -93,62 +77,60 @@ public class DoStatement extends Statement { return null; } - public String toJava(int indent, BytecodeMappingTracer tracer) { - String indstr = InterpreterUtil.getIndentString(indent); - StringBuilder buf = new StringBuilder(); + @Override + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + TextBuffer buf = new TextBuffer(); - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - String content = ExprProcessor.listToJava(varDefinitions, indent, tracer); - buf.append(content); - if (!content.isEmpty()) { - buf.append(new_line_separator); - } + buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer)); if (isLabeled()) { - buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator); + buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } switch (looptype) { case LOOP_DO: - buf.append(indstr).append("while (true) {").append(new_line_separator); + buf.appendIndent(indent).append("while(true) {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); - buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true, tracer)); - buf.append(indstr).append("}").append(new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent + 1, false, tracer)); + buf.appendIndent(indent).append("}").appendLineSeparator(); tracer.incrementCurrentSourceLine(); break; case LOOP_DOWHILE: - buf.append(indstr).append("do {").append(new_line_separator); + buf.appendIndent(indent).append("do {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); - buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true, tracer)); - buf.append(indstr).append("} while (").append(conditionExprent.get(0).toJava(indent, tracer)).append(");").append(new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent + 1, false, tracer)); + buf.appendIndent(indent).append("} while(").append(conditionExprent.get(0).toJava(indent, tracer)).append(");").appendLineSeparator(); tracer.incrementCurrentSourceLine(); break; case LOOP_WHILE: - buf.append(indstr).append("while (").append(conditionExprent.get(0).toJava(indent, tracer)).append(") {").append(new_line_separator); + buf.appendIndent(indent).append("while(").append(conditionExprent.get(0).toJava(indent, tracer)).append(") {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); - buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true, tracer)); - buf.append(indstr).append("}").append(new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent + 1, false, tracer)); + buf.appendIndent(indent).append("}").appendLineSeparator(); tracer.incrementCurrentSourceLine(); break; case LOOP_FOR: - buf.append(indstr).append("for (").append(initExprent.get(0) == null ? "" : initExprent.get(0).toJava(indent, tracer)).append("; ") + buf.appendIndent(indent).append("for("); + if (initExprent.get(0) != null) { + buf.append(initExprent.get(0).toJava(indent, tracer)); + } + buf.append("; ") .append(conditionExprent.get(0).toJava(indent, tracer)).append("; ").append(incExprent.get(0).toJava(indent, tracer)).append(") {") - .append(new_line_separator); + .appendLineSeparator(); tracer.incrementCurrentSourceLine(); - buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true, tracer)); - buf.append(indstr).append("}").append(new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent + 1, false, tracer)); + buf.appendIndent(indent).append("}").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } - // buf.append(new_line_separator); - return buf.toString(); + return buf; } + @Override public List getSequentialObjects() { - List lst = new ArrayList(); + List lst = new ArrayList<>(); switch (looptype) { case LOOP_FOR: @@ -172,6 +154,7 @@ public class DoStatement extends Statement { return lst; } + @Override public void replaceExprent(Exprent oldexpr, Exprent newexpr) { if (initExprent.get(0) == oldexpr) { initExprent.set(0, newexpr); @@ -184,6 +167,7 @@ public class DoStatement extends Statement { } } + @Override public Statement getSimpleCopy() { return new DoStatement(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DummyExitStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DummyExitStatement.java new file mode 100644 index 0000000..c1d4530 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DummyExitStatement.java @@ -0,0 +1,25 @@ +// 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 java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +public class DummyExitStatement extends Statement { + public Set bytecode = null; // offsets of bytecode instructions mapped to dummy exit + + public DummyExitStatement() { + type = Statement.TYPE_DUMMYEXIT; + } + + public void addBytecodeOffsets(Collection bytecodeOffsets) { + if (bytecodeOffsets != null && !bytecodeOffsets.isEmpty()) { + if (bytecode == null) { + bytecode = new HashSet<>(bytecodeOffsets); + } + else { + bytecode.addAll(bytecodeOffsets); + } + } + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java index 8541e68..0891a47 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java @@ -1,23 +1,8 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.stats; -import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; -import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.Collection; import java.util.HashSet; @@ -33,14 +18,14 @@ public class GeneralStatement extends Statement { type = Statement.TYPE_GENERAL; } - public GeneralStatement(Statement head, Collection statements, Statement post) { + public GeneralStatement(Statement head, Collection statements, Statement post) { this(); first = head; stats.addWithKey(head, head.id); - HashSet set = new HashSet(statements); + HashSet set = new HashSet<>(statements); set.remove(head); for (Statement st : set) { @@ -54,22 +39,20 @@ public class GeneralStatement extends Statement { // public methods // ***************************************************************************** - public String toJava(int indent, BytecodeMappingTracer tracer) { - String indstr = InterpreterUtil.getIndentString(indent); - StringBuilder buf = new StringBuilder(); - - String new_line_separator = DecompilerContext.getNewLineSeparator(); + @Override + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + TextBuffer buf = new TextBuffer(); if (isLabeled()) { - buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator); + buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); } - buf.append(indstr).append("abstract statement {").append(new_line_separator); - for (int i = 0; i < stats.size(); i++) { - buf.append(stats.get(i).toJava(indent + 1, tracer)); + buf.appendIndent(indent).append("abstract statement {").appendLineSeparator(); + for (Statement stat : stats) { + buf.append(stat.toJava(indent + 1, tracer)); } - buf.append(indstr).append("}"); + buf.appendIndent(indent).append("}"); - return buf.toString(); + return buf; } } 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 3e9d368..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,37 +1,26 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; -import org.jetbrains.java.decompiler.modules.decompiler.exps.*; -import org.jetbrains.java.decompiler.util.InterpreterUtil; -import org.jetbrains.java.decompiler.util.Util; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; +import org.jetbrains.java.decompiler.struct.match.IMatchable; +import org.jetbrains.java.decompiler.struct.match.MatchEngine; +import org.jetbrains.java.decompiler.struct.match.MatchNode; +import org.jetbrains.java.decompiler.util.TextBuffer; +import org.jetbrains.java.decompiler.util.TextUtil; import java.util.ArrayList; import java.util.List; -public class IfStatement extends Statement { +public final class IfStatement extends Statement { - public static int IFTYPE_IF = 0; - public static int IFTYPE_IFELSE = 1; + public static final int IFTYPE_IF = 0; + public static final int IFTYPE_IFELSE = 1; public int iftype; @@ -47,9 +36,7 @@ public class IfStatement extends Statement { private boolean negated = false; - private boolean iffflag; - - private List headexprent = new ArrayList(); // contains IfExprent + private final List headexprent = new ArrayList<>(1); // contains IfExprent // ***************************************************************************** // constructors @@ -178,7 +165,7 @@ public class IfStatement extends Statement { boolean ok = (regsize < 2); if (!ok) { - List lst = new ArrayList(); + List lst = new ArrayList<>(); if (DecHelper.isChoiceStatement(head, lst)) { p = lst.remove(0); @@ -200,75 +187,42 @@ public class IfStatement extends Statement { return null; } - public String toJava(int indent, BytecodeMappingTracer tracer) { - String indstr = InterpreterUtil.getIndentString(indent); - StringBuilder buf = new StringBuilder(); - - String new_line_separator = DecompilerContext.getNewLineSeparator(); + @Override + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + TextBuffer buf = new TextBuffer(); buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer)); - - boolean bloodySpecialCases = false; - if (first instanceof BasicBlockStatement) { - List exps = first.getExprents(); - if (exps.size() != 0) { - Exprent last = exps.get(exps.size() - 1); - if (last instanceof AssignmentExprent) { - AssignmentExprent assignmentExprent = (AssignmentExprent) exps.get(exps.size() - 1); - bloodySpecialCases = true; - if (assignmentExprent.getLeft() instanceof VarExprent) { - VarExprent var = (VarExprent) assignmentExprent.getLeft(); - bloodySpecialCases = !var.isDefinition(); - } - } else if (last instanceof InvocationExprent) { - bloodySpecialCases = true; - } - } - } - - if (bloodySpecialCases) { - buf.append(Util.rtrim(first.toJava(indent, tracer))).append(new_line_separator); - } else { - String content = first.toJava(indent, tracer); - buf.append(content); - if (first instanceof BasicBlockStatement && !content.isEmpty()) { - List exps = first.getExprents(); - if (exps.size() != 0) { - Exprent e = exps.get(exps.size() - 1); - if (!(e instanceof InvocationExprent || e instanceof FunctionExprent)) { - buf.append(new_line_separator); - } - } - } - } + buf.append(first.toJava(indent, tracer)); if (isLabeled()) { - buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator); + buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } - buf.append(indstr).append(headexprent.get(0).toJava(indent, tracer)).append(" {").append(new_line_separator); + buf.appendIndent(indent).append(headexprent.get(0).toJava(indent, tracer)).append(" {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); if (ifstat == null) { - buf.append(InterpreterUtil.getIndentString(indent + 1)); - + boolean semicolon = false; if (ifedge.explicit) { + semicolon = true; if (ifedge.getType() == StatEdge.TYPE_BREAK) { // break - buf.append("break"); + buf.appendIndent(indent + 1).append("break"); } else { // continue - buf.append("continue"); + buf.appendIndent(indent + 1).append("continue"); } if (ifedge.labeled) { - buf.append(" label").append(ifedge.closure.id); + buf.append(" label").append(ifedge.closure.id.toString()); } } - buf.append(";").append(new_line_separator); - tracer.incrementCurrentSourceLine(); + if(semicolon) { + buf.append(";").appendLineSeparator(); + tracer.incrementCurrentSourceLine(); + } } else { buf.append(ExprProcessor.jmpWrapper(ifstat, indent + 1, true, tracer)); @@ -282,23 +236,22 @@ public class IfStatement extends Statement { !elsestat.isLabeled() && (elsestat.getSuccessorEdges(STATEDGE_DIRECT_ALL).isEmpty() || !elsestat.getSuccessorEdges(STATEDGE_DIRECT_ALL).get(0).explicit)) { // else if - String content = ExprProcessor.jmpWrapper(elsestat, indent, false, tracer); - content = content.substring(indstr.length()); + buf.appendIndent(indent).append("} else "); - buf.append(indstr).append("} else "); + TextBuffer content = ExprProcessor.jmpWrapper(elsestat, indent, false, tracer); + content.setStart(TextUtil.getIndentString(indent).length()); buf.append(content); elseif = true; } else { - BytecodeMappingTracer else_tracer = new BytecodeMappingTracer(tracer.getCurrentSourceLine()); - String content = ExprProcessor.jmpWrapper(elsestat, indent + 1, false, else_tracer); + BytecodeMappingTracer else_tracer = new BytecodeMappingTracer(tracer.getCurrentSourceLine() + 1); + TextBuffer content = ExprProcessor.jmpWrapper(elsestat, indent + 1, false, else_tracer); if (content.length() > 0) { - buf.append(indstr).append("} else {").append(new_line_separator); + buf.appendIndent(indent).append("} else {").appendLineSeparator(); - else_tracer.shiftSourceLines(1); - tracer.setCurrentSourceLine(else_tracer.getCurrentSourceLine() + 1); + tracer.setCurrentSourceLine(else_tracer.getCurrentSourceLine()); tracer.addTracer(else_tracer); buf.append(content); @@ -307,13 +260,14 @@ public class IfStatement extends Statement { } if (!elseif) { - buf.append(indstr).append("}").append(new_line_separator); + buf.appendIndent(indent).append("}").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } - return buf.toString(); + return buf; } + @Override public void initExprents() { IfExprent ifexpr = (IfExprent)first.getExprents().remove(first.getExprents().size() - 1); @@ -326,20 +280,23 @@ public class IfStatement extends Statement { headexprent.set(0, ifexpr); } + @Override public List getSequentialObjects() { - List lst = new ArrayList(stats); + List lst = new ArrayList<>(stats); lst.add(1, headexprent.get(0)); return lst; } + @Override public void replaceExprent(Exprent oldexpr, Exprent newexpr) { if (headexprent.get(0) == oldexpr) { headexprent.set(0, newexpr); } } + @Override public void replaceStatement(Statement oldstat, Statement newstat) { super.replaceStatement(oldstat, newstat); @@ -372,16 +329,17 @@ public class IfStatement extends Statement { } } + @Override public Statement getSimpleCopy() { IfStatement is = new IfStatement(); is.iftype = this.iftype; is.negated = this.negated; - is.iffflag = this.iffflag; return is; } + @Override public void initSimpleCopy() { first = stats.get(0); @@ -434,14 +392,6 @@ public class IfStatement extends Statement { return (IfExprent)headexprent.get(0); } - public boolean isIffflag() { - return iffflag; - } - - public void setIffflag(boolean iffflag) { - this.iffflag = iffflag; - } - public void setElseEdge(StatEdge elseedge) { this.elseedge = elseedge; } @@ -457,4 +407,35 @@ public class IfStatement extends Statement { public StatEdge getElseEdge() { return elseedge; } -} + + // ***************************************************************************** + // IMatchable implementation + // ***************************************************************************** + + @Override + public IMatchable findObject(MatchNode matchNode, int index) { + IMatchable object = super.findObject(matchNode, index); + if (object != null) { + return object; + } + + if (matchNode.getType() == MatchNode.MATCHNODE_EXPRENT) { + String position = (String)matchNode.getRuleValue(MatchProperties.EXPRENT_POSITION); + if ("head".equals(position)) { + return getHeadexprent(); + } + } + + return null; + } + + @Override + public boolean match(MatchNode matchNode, MatchEngine engine) { + if (!super.match(matchNode, engine)) { + return false; + } + + Integer type = (Integer)matchNode.getRuleValue(MatchProperties.STATEMENT_IFTYPE); + return type == null || this.iftype == type; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java index 58ec365..61eca33 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java @@ -1,30 +1,14 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.stats; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; - +import org.jetbrains.java.decompiler.util.TextBuffer; public class RootStatement extends Statement { + private final DummyExitStatement dummyExit; - private Statement dummyExit; - - public RootStatement(Statement head, Statement dummyExit) { - + public RootStatement(Statement head, DummyExitStatement dummyExit) { type = Statement.TYPE_ROOT; first = head; @@ -34,16 +18,12 @@ public class RootStatement extends Statement { first.setParent(this); } - public String toJava(int indent, BytecodeMappingTracer tracer) { - return ExprProcessor.listToJava(varDefinitions, indent, tracer) + - first.toJava(indent, tracer); + @Override + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + return ExprProcessor.listToJava(varDefinitions, indent, tracer).append(first.toJava(indent, tracer)); } - public Statement getDummyExit() { + public DummyExitStatement getDummyExit() { return dummyExit; } - - public void setDummyExit(Statement dummyExit) { - this.dummyExit = dummyExit; - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java index 38420e0..8aa6adc 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java @@ -1,26 +1,11 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.stats; -import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; -import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.Arrays; import java.util.List; @@ -37,7 +22,7 @@ public class SequenceStatement extends Statement { type = Statement.TYPE_SEQUENCE; } - public SequenceStatement(List lst) { + public SequenceStatement(List lst) { this(); @@ -99,21 +84,15 @@ public class SequenceStatement extends Statement { return null; } - public String toJava(int indent, BytecodeMappingTracer tracer) { - - StringBuilder buf = new StringBuilder(); - - String indstr = null; + @Override + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + TextBuffer buf = new TextBuffer(); boolean islabeled = isLabeled(); - String new_line_separator = DecompilerContext.getNewLineSeparator(); - buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer)); if (islabeled) { - indstr = InterpreterUtil.getIndentString(indent); - indent++; - buf.append(indstr).append("label").append(this.id).append(": {").append(new_line_separator); + buf.appendIndent(indent++).append("label").append(this.id.toString()).append(": {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } @@ -124,24 +103,25 @@ public class SequenceStatement extends Statement { Statement st = stats.get(i); if (i > 0 && notempty) { - buf.append(new_line_separator); + buf.appendLineSeparator(); tracer.incrementCurrentSourceLine(); } - String str = ExprProcessor.jmpWrapper(st, indent, false, tracer); + TextBuffer str = ExprProcessor.jmpWrapper(st, indent, false, tracer); buf.append(str); - notempty = (str.trim().length() > 0); + notempty = !str.containsOnlyWhitespaces(); } if (islabeled) { - buf.append(indstr).append("}").append(new_line_separator); + buf.appendIndent(indent-1).append("}").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } - return buf.toString(); + return buf; } + @Override public Statement getSimpleCopy() { return new SequenceStatement(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java index e9eae20..b5e8213 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java @@ -1,36 +1,29 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ package org.jetbrains.java.decompiler.modules.decompiler.stats; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.InstructionSequence; import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.util.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.StrongConnectivityHelper; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.struct.match.IMatchable; +import org.jetbrains.java.decompiler.struct.match.MatchEngine; +import org.jetbrains.java.decompiler.struct.match.MatchNode; +import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue; import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.util.*; +import java.util.Map.Entry; -public class Statement { - - public static final int STATEDGE_ALL = 1 << 31; - public static final int STATEDGE_DIRECT_ALL = 1 << 30; +public class Statement implements IMatchable { + public static final int STATEDGE_ALL = 0x80000000; + public static final int STATEDGE_DIRECT_ALL = 0x40000000; public static final int DIRECTION_BACKWARD = 0; public static final int DIRECTION_FORWARD = 1; @@ -41,7 +34,7 @@ public class Statement { public static final int TYPE_SWITCH = 6; public static final int TYPE_TRYCATCH = 7; public static final int TYPE_BASICBLOCK = 8; - public static final int TYPE_FINALLY = 9; + //public static final int TYPE_FINALLY = 9; public static final int TYPE_SYNCRONIZED = 10; public static final int TYPE_PLACEHOLDER = 11; public static final int TYPE_CATCHALL = 12; @@ -49,7 +42,6 @@ public class Statement { public static final int TYPE_DUMMYEXIT = 14; public static final int TYPE_SEQUENCE = 15; - public static final int LASTBASICTYPE_IF = 0; public static final int LASTBASICTYPE_SWITCH = 1; public static final int LASTBASICTYPE_GENERAL = 2; @@ -67,14 +59,14 @@ public class Statement { // private fields // ***************************************************************************** - private Map> mapSuccEdges = new HashMap>(); - private Map> mapPredEdges = new HashMap>(); + private final Map> mapSuccEdges = new HashMap<>(); + private final Map> mapPredEdges = new HashMap<>(); - private Map> mapSuccStates = new HashMap>(); - private Map> mapPredStates = new HashMap>(); + private final Map> mapSuccStates = new HashMap<>(); + private final Map> mapPredStates = new HashMap<>(); // statement as graph - protected VBStyleCollection stats = new VBStyleCollection(); + protected final VBStyleCollection stats = new VBStyleCollection<>(); protected Statement parent; @@ -82,9 +74,9 @@ public class Statement { protected List exprents; - protected HashSet labelEdges = new HashSet(); + protected final HashSet labelEdges = new HashSet<>(); - protected List varDefinitions = new ArrayList(); + protected final List varDefinitions = new ArrayList<>(); // copied statement, s. deobfuscating of irreducible CFGs private boolean copied = false; @@ -100,7 +92,7 @@ public class Statement { protected boolean containsMonitorExit; - protected HashSet continueSet = new HashSet(); + protected HashSet continueSet = new HashSet<>(); // ***************************************************************************** // initializers @@ -137,7 +129,7 @@ public class Statement { List lst = map.get(STATEDGE_DIRECT_ALL); if (lst != null) { - map.put(STATEDGE_ALL, new ArrayList(lst)); + map.put(STATEDGE_ALL, new ArrayList<>(lst)); } else { map.remove(STATEDGE_ALL); @@ -180,7 +172,7 @@ public class Statement { } // exception edges - Set setHandlers = new HashSet(head.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)); + Set setHandlers = new HashSet<>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)); for (Statement node : setNodes) { setHandlers.retainAll(node.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)); } @@ -248,25 +240,15 @@ public class Statement { } private void addEdgeDirectInternal(int direction, StatEdge edge, int edgetype) { - Map> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges; Map> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates; - List lst = mapEdges.get(edgetype); - if (lst == null) { - mapEdges.put(edgetype, lst = new ArrayList()); - } - lst.add(edge); + mapEdges.computeIfAbsent(edgetype, k -> new ArrayList<>()).add(edge); - List lstStates = mapStates.get(edgetype); - if (lstStates == null) { - mapStates.put(edgetype, lstStates = new ArrayList()); - } - lstStates.add(direction == DIRECTION_BACKWARD ? edge.getSource() : edge.getDestination()); + mapStates.computeIfAbsent(edgetype, k -> new ArrayList<>()).add(direction == DIRECTION_BACKWARD ? edge.getSource() : edge.getDestination()); } private void addEdgeInternal(int direction, StatEdge edge) { - int type = edge.getType(); int[] arrtypes; @@ -436,7 +418,7 @@ public class Statement { } public List getReversePostOrderList(Statement stat) { - List res = new ArrayList(); + List res = new ArrayList<>(); addToReversePostOrderListIterative(stat, res); @@ -449,14 +431,14 @@ public class Statement { public List getPostReversePostOrderList(List lstexits) { - List res = new ArrayList(); + List res = new ArrayList<>(); if (lstexits == null) { StrongConnectivityHelper schelper = new StrongConnectivityHelper(this); lstexits = StrongConnectivityHelper.getExitReps(schelper.getComponents()); } - HashSet setVisited = new HashSet(); + HashSet setVisited = new HashSet<>(); for (Statement exit : lstexits) { addToPostReversePostOrderList(exit, res, setVisited); @@ -474,13 +456,12 @@ public class Statement { } public boolean containsStatementStrict(Statement stat) { - if (stats.contains(stat)) { return true; } - for (int i = 0; i < stats.size(); i++) { - if (stats.get(i).containsStatementStrict(stat)) { + for (Statement st : stats) { + if (st.containsStatementStrict(stat)) { return true; } } @@ -488,18 +469,13 @@ public class Statement { return false; } - // to be overwritten - public String toJava() { - return toJava(0, new BytecodeMappingTracer()); - } - - public String toJava(int indent, BytecodeMappingTracer tracer) { + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { throw new RuntimeException("not implemented"); } // TODO: make obsolete and remove public List getSequentialObjects() { - return new ArrayList(stats); + return new ArrayList<>(stats); } public void initExprents() { @@ -545,7 +521,7 @@ public class Statement { first = newstat; } - List lst = new ArrayList(oldstat.getLabelEdges()); + List lst = new ArrayList<>(oldstat.getLabelEdges()); for (int i = lst.size() - 1; i >= 0; i--) { StatEdge edge = lst.get(i); @@ -570,11 +546,11 @@ public class Statement { // private methods // ***************************************************************************** - private static void addToReversePostOrderListIterative(Statement root, List lst) { + private static void addToReversePostOrderListIterative(Statement root, List lst) { - LinkedList stackNode = new LinkedList(); - LinkedList stackIndex = new LinkedList(); - HashSet setVisited = new HashSet(); + LinkedList stackNode = new LinkedList<>(); + LinkedList stackIndex = new LinkedList<>(); + HashSet setVisited = new HashSet<>(); stackNode.add(root); stackIndex.add(0); @@ -613,7 +589,7 @@ public class Statement { } - private static void addToPostReversePostOrderList(Statement stat, List lst, HashSet setVisited) { + private static void addToPostReversePostOrderList(Statement stat, List lst, HashSet setVisited) { if (setVisited.contains(stat)) { // because of not considered exception edges, s. isExitComponent. Should be rewritten, if possible. return; @@ -696,10 +672,10 @@ public class Statement { List res; if ((type & (type - 1)) == 0) { res = map.get(type); - res = res == null ? new ArrayList() : new ArrayList(res); + res = res == null ? new ArrayList<>() : new ArrayList<>(res); } else { - res = new ArrayList(); + res = new ArrayList<>(); for (int edgetype : StatEdge.TYPES) { if ((type & edgetype) != 0) { List lst = map.get(edgetype); @@ -720,10 +696,10 @@ public class Statement { List res; if ((type & (type - 1)) == 0) { res = map.get(type); - res = res == null ? new ArrayList() : new ArrayList(res); + res = res == null ? new ArrayList<>() : new ArrayList<>(res); } else { - res = new ArrayList(); + res = new ArrayList<>(); for (int edgetype : StatEdge.TYPES) { if ((type & edgetype) != 0) { List lst = map.get(edgetype); @@ -738,7 +714,7 @@ public class Statement { } public Set getNeighboursSet(int type, int direction) { - return new HashSet(getNeighbours(type, direction)); + return new HashSet<>(getNeighbours(type, direction)); } public List getSuccessorEdges(int type) { @@ -769,10 +745,6 @@ public class Statement { return post; } - public void setPost(Statement post) { - this.post = post; - } - public VBStyleCollection getStats() { return stats; } @@ -858,4 +830,80 @@ public class Statement { public String toString() { return id.toString(); } -} + + // ***************************************************************************** + // IMatchable implementation + // ***************************************************************************** + + @Override + public IMatchable findObject(MatchNode matchNode, int index) { + int node_type = matchNode.getType(); + + if (node_type == MatchNode.MATCHNODE_STATEMENT && !this.stats.isEmpty()) { + String position = (String)matchNode.getRuleValue(MatchProperties.STATEMENT_POSITION); + if (position != null) { + if (position.matches("-?\\d+")) { + return this.stats.get((this.stats.size() + Integer.parseInt(position)) % this.stats.size()); // care for negative positions + } + } + else if (index < this.stats.size()) { // use 'index' parameter + return this.stats.get(index); + } + } + else if (node_type == MatchNode.MATCHNODE_EXPRENT && this.exprents != null && !this.exprents.isEmpty()) { + String position = (String)matchNode.getRuleValue(MatchProperties.EXPRENT_POSITION); + if (position != null) { + if (position.matches("-?\\d+")) { + return this.exprents.get((this.exprents.size() + Integer.parseInt(position)) % this.exprents.size()); // care for negative positions + } + } + else if (index < this.exprents.size()) { // use 'index' parameter + return this.exprents.get(index); + } + } + + return null; + } + + @Override + public boolean match(MatchNode matchNode, MatchEngine engine) { + if (matchNode.getType() != MatchNode.MATCHNODE_STATEMENT) { + return false; + } + + for (Entry rule : matchNode.getRules().entrySet()) { + switch (rule.getKey()) { + case STATEMENT_TYPE: + if (this.type != (Integer)rule.getValue().value) { + return false; + } + break; + case STATEMENT_STATSIZE: + if (this.stats.size() != (Integer)rule.getValue().value) { + return false; + } + break; + case STATEMENT_EXPRSIZE: + int exprsize = (Integer)rule.getValue().value; + if (exprsize == -1) { + if (this.exprents != null) { + return false; + } + } + else { + if (this.exprents == null || this.exprents.size() != exprsize) { + return false; + } + } + break; + case STATEMENT_RET: + if (!engine.checkAndSetVariableValue((String)rule.getValue().value, this)) { + return false; + } + break; + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java new file mode 100644 index 0000000..891ba35 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java @@ -0,0 +1,44 @@ +// 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; +import org.jetbrains.java.decompiler.main.rels.MethodWrapper; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; + +public final class Statements { + public static Statement findFirstData(Statement stat) { + if (stat.getExprents() != null) { + return stat; + } + else if (stat.isLabeled()) { // FIXME: Why?? + return null; + } + + switch (stat.type) { + case Statement.TYPE_SEQUENCE: + case Statement.TYPE_IF: + case Statement.TYPE_ROOT: + case Statement.TYPE_SWITCH: + case Statement.TYPE_SYNCRONIZED: + return findFirstData(stat.getFirst()); + default: + return null; + } + } + + public static boolean isInvocationInitConstructor(InvocationExprent inv, MethodWrapper method, ClassWrapper wrapper, boolean withThis) { + if (inv.getFunctype() == InvocationExprent.TYP_INIT && inv.getInstance().type == Exprent.EXPRENT_VAR) { + VarExprent instVar = (VarExprent)inv.getInstance(); + VarVersionPair varPair = new VarVersionPair(instVar); + String className = method.varproc.getThisVars().get(varPair); + if (className != null) { // any this instance. TODO: Restrict to current class? + return withThis || !wrapper.getClassStruct().qualifiedName.equals(inv.getClassname()); + } + } + + return false; + } +} \ No newline at end of file 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 694baae..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,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -23,29 +9,31 @@ import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; -import org.jetbrains.java.decompiler.modules.decompiler.exps.*; -import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.modules.decompiler.SwitchHelper; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchExprent; import org.jetbrains.java.decompiler.struct.gen.VarType; -import org.jetbrains.java.decompiler.util.InterpreterUtil; -import org.jetbrains.java.decompiler.util.Util; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.*; -public class SwitchStatement extends Statement { +public final class SwitchStatement extends Statement { // ***************************************************************************** // private fields // ***************************************************************************** - private List caseStatements = new ArrayList(); + private List caseStatements = new ArrayList<>(); - private List> caseEdges = new ArrayList>(); + private List> caseEdges = new ArrayList<>(); - private List> caseValues = new ArrayList>(); + private List> caseValues = new ArrayList<>(); private StatEdge default_edge; - private List headexprent = new ArrayList(); + private final List headexprent = new ArrayList<>(1); // ***************************************************************************** // constructors @@ -65,7 +53,7 @@ public class SwitchStatement extends Statement { stats.addWithKey(head, head.id); // find post node - Set lstNodes = new HashSet(head.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_FORWARD)); + Set lstNodes = new HashSet<>(head.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_FORWARD)); // cluster nodes if (poststat != null) { @@ -88,7 +76,7 @@ public class SwitchStatement extends Statement { if (head.type == Statement.TYPE_BASICBLOCK && head.getLastBasicType() == Statement.LASTBASICTYPE_SWITCH) { - List lst = new ArrayList(); + List lst = new ArrayList<>(); if (DecHelper.isChoiceStatement(head, lst)) { Statement post = lst.remove(0); @@ -107,43 +95,20 @@ public class SwitchStatement extends Statement { return null; } - public String toJava(int indent, BytecodeMappingTracer tracer) { + @Override + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + SwitchHelper.simplify(this); - String indstr = InterpreterUtil.getIndentString(indent); - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - StringBuilder buf = new StringBuilder(); - String content = ExprProcessor.listToJava(varDefinitions, indent, tracer); - buf.append(content); - if (!content.isEmpty()) { - buf.append(new_line_separator); - } - content = first.toJava(indent, tracer); - buf.append(content); - if (first instanceof BasicBlockStatement && !content.isEmpty()) { - List exps = first.getExprents(); - if (exps.size() != 0) { - Exprent e = exps.get(exps.size() - 1); - if (!(e instanceof InvocationExprent - || e instanceof FunctionExprent - || (e instanceof AssignmentExprent && !(((AssignmentExprent) e).getLeft() instanceof VarExprent && ((VarExprent) ((AssignmentExprent) e).getLeft()).isDefinition())))) { - buf.append(new_line_separator); - } - } - } + TextBuffer buf = new TextBuffer(); + buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer)); + buf.append(first.toJava(indent, tracer)); if (isLabeled()) { - buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator); + buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } - // Doesn't seem to be a better place to put it so enhance things here - Map remaps = enhanceHead(headexprent.get(0), buf, indent, tracer); - - if (remaps == null) { - buf.append(indstr).append(headexprent.get(0).toJava(indent, tracer)).append(" {").append(new_line_separator); - } + buf.appendIndent(indent).append(headexprent.get(0).toJava(indent, tracer)).append(" {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); VarType switch_type = headexprent.get(0).getExprType(); @@ -152,80 +117,41 @@ public class SwitchStatement extends Statement { Statement stat = caseStatements.get(i); List edges = caseEdges.get(i); - List values = caseValues.get(i); + List values = caseValues.get(i); for (int j = 0; j < edges.size(); j++) { if (edges.get(j) == default_edge) { - buf.append(indstr).append("default:").append(new_line_separator); - tracer.incrementCurrentSourceLine(); + buf.appendIndent(indent).append("default:").appendLineSeparator(); } else { - ConstExprent value = (ConstExprent)values.get(j).copy(); - value.setConsttype(switch_type); - - buf.append(indstr).append("case "); - if (remaps == null) { - buf.append(value.toJava(indent, tracer)); - } else { - buf.append(remaps.get(value.getValue())); + buf.appendIndent(indent).append("case "); + Exprent value = values.get(j); + if (value instanceof ConstExprent) { + value = value.copy(); + ((ConstExprent)value).setConstType(switch_type); } - buf.append(":").append(new_line_separator); - tracer.incrementCurrentSourceLine(); + if (value instanceof FieldExprent && ((FieldExprent)value).isStatic()) { // enum values + buf.append(((FieldExprent)value).getName()); + } + else { + buf.append(value.toJava(indent, tracer)); + } + + buf.append(":").appendLineSeparator(); } + tracer.incrementCurrentSourceLine(); } - String c = Util.rtrim(ExprProcessor.jmpWrapper(stat, indent + 1, false, tracer)); - if (!c.isEmpty()) { - buf.append(c); - buf.append(new_line_separator); - } - if (i != caseStatements.size() - 1) buf.append(new_line_separator); + buf.append(ExprProcessor.jmpWrapper(stat, indent + 1, false, tracer)); } - buf.append(indstr).append("}").append(new_line_separator); + buf.appendIndent(indent).append("}").appendLineSeparator(); tracer.incrementCurrentSourceLine(); - return buf.toString(); - } - - private Map enhanceHead(Exprent exprent, StringBuilder buf, int indent, BytecodeMappingTracer tracer) { - if (exprent.type != Exprent.EXPRENT_SWITCH) return null; - - SwitchExprent swtch = (SwitchExprent)exprent; - if (swtch.getValue().type != Exprent.EXPRENT_ARRAY) return null; - - ArrayExprent array = (ArrayExprent)swtch.getValue(); - if (array.getArray().type != Exprent.EXPRENT_FIELD || array.getIndex().type != Exprent.EXPRENT_INVOCATION) return null; - - FieldExprent field = (FieldExprent)array.getArray(); - InvocationExprent invoc = (InvocationExprent)array.getIndex(); - StructClass cls = DecompilerContext.getStructContext().getClass(field.getClassname()); - if (cls == null || !field.isStatic() || !"ordinal".equals(invoc.getName()) || !"()I".equals(invoc.getStringDescriptor())) return null; - - Map ret = cls.enumSwitchMap.get(field.getName()); - if (ret == null) return null; - - for (List lst : getCaseValues()) { - if (lst != null) { - for (ConstExprent cst : lst) { - if (cst != null && (!(cst.getValue() instanceof Integer) || !ret.containsKey(cst.getValue()))) { - return null; - } - } - } - } - - tracer.addMapping(swtch.bytecode); - tracer.addMapping(field.bytecode); - tracer.addMapping(invoc.bytecode); - - String indstr = InterpreterUtil.getIndentString(indent); - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - buf.append(indstr).append("switch (").append((invoc.getInstance().toJava(indent, tracer))).append(") {").append(new_line_separator); - return ret; + return buf; } + @Override public void initExprents() { SwitchExprent swexpr = (SwitchExprent)first.getExprents().remove(first.getExprents().size() - 1); swexpr.setCaseValues(caseValues); @@ -233,20 +159,23 @@ public class SwitchStatement extends Statement { headexprent.set(0, swexpr); } + @Override public List getSequentialObjects() { - List lst = new ArrayList(stats); + List lst = new ArrayList<>(stats); lst.add(1, headexprent.get(0)); return lst; } + @Override public void replaceExprent(Exprent oldexpr, Exprent newexpr) { if (headexprent.get(0) == oldexpr) { headexprent.set(0, newexpr); } } + @Override public void replaceStatement(Statement oldstat, Statement newstat) { for (int i = 0; i < caseStatements.size(); i++) { @@ -258,10 +187,12 @@ public class SwitchStatement extends Statement { super.replaceStatement(oldstat, newstat); } + @Override public Statement getSimpleCopy() { return new SwitchStatement(); } + @Override public void initSimpleCopy() { first = stats.get(0); default_edge = first.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0); @@ -275,7 +206,7 @@ public class SwitchStatement extends Statement { public void sortEdgesAndNodes() { - HashMap mapEdgeIndex = new HashMap(); + HashMap mapEdgeIndex = new HashMap<>(); List lstFirstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); for (int i = 0; i < lstFirstSuccs.size(); i++) { @@ -286,15 +217,15 @@ public class SwitchStatement extends Statement { BasicBlockStatement bbstat = (BasicBlockStatement)first; int[] values = ((SwitchInstruction)bbstat.getBlock().getLastInstruction()).getValues(); - List nodes = new ArrayList(); - List> edges = new ArrayList>(); + List nodes = new ArrayList<>(stats.size() - 1); + List> edges = new ArrayList<>(stats.size() - 1); // collect regular edges for (int i = 1; i < stats.size(); i++) { Statement stat = stats.get(i); - List lst = new ArrayList(); + List lst = new ArrayList<>(); for (StatEdge edge : stat.getPredecessorEdges(StatEdge.TYPE_REGULAR)) { if (edge.getSource() == first) { lst.add(mapEdgeIndex.get(edge)); @@ -311,7 +242,7 @@ public class SwitchStatement extends Statement { while (!lstExitEdges.isEmpty()) { StatEdge edge = lstExitEdges.get(0); - List lst = new ArrayList(); + List lst = new ArrayList<>(); for (int i = lstExitEdges.size() - 1; i >= 0; i--) { StatEdge edgeTemp = lstExitEdges.get(i); if (edgeTemp.getDestination() == edge.getDestination() && edgeTemp.getType() == edge.getType()) { @@ -340,7 +271,7 @@ public class SwitchStatement extends Statement { Statement stat = nodes.get(index); if (stat != null) { - HashSet setPreds = new HashSet(stat.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_BACKWARD)); + HashSet setPreds = new HashSet<>(stat.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_BACKWARD)); setPreds.remove(first); if (!setPreds.isEmpty()) { @@ -368,19 +299,19 @@ public class SwitchStatement extends Statement { } // translate indices back into edges - List> lstEdges = new ArrayList>(); - List> lstValues = new ArrayList>(); + List> lstEdges = new ArrayList<>(edges.size()); + List> lstValues = new ArrayList<>(edges.size()); for (List lst : edges) { - List lste = new ArrayList(); - List lstv = new ArrayList(); + List lste = new ArrayList<>(lst.size()); + List lstv = new ArrayList<>(lst.size()); List lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); for (Integer in : lst) { int index = in == lstSuccs.size() ? 0 : in; lste.add(lstSuccs.get(index)); - lstv.add(index == 0 ? null : new ConstExprent(values[index - 1], false)); + lstv.add(index == 0 ? null : new ConstExprent(values[index - 1], false, null)); } lstEdges.add(lste); lstValues.add(lstv); @@ -437,7 +368,7 @@ public class SwitchStatement extends Statement { return default_edge; } - public List> getCaseValues() { + public List> getCaseValues() { return caseValues; } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java index 3ac68a6..bc56583 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java @@ -1,27 +1,14 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.stats; -import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.SequenceHelper; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; -import org.jetbrains.java.decompiler.modules.decompiler.exps.*; -import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.ArrayList; import java.util.List; @@ -31,7 +18,7 @@ public class SynchronizedStatement extends Statement { private Statement body; - private List headexprent = new ArrayList(); + private final List headexprent = new ArrayList<>(1); // ***************************************************************************** // constructors @@ -69,68 +56,59 @@ public class SynchronizedStatement extends Statement { // public methods // ***************************************************************************** - public String toJava(int indent, BytecodeMappingTracer tracer) { - String indstr = InterpreterUtil.getIndentString(indent); - - String new_line_separator = DecompilerContext.getNewLineSeparator(); - - StringBuilder buf = new StringBuilder(); - - String content = ExprProcessor.listToJava(varDefinitions, indent, tracer); - buf.append(content); - if (!content.isEmpty()) { - buf.append(new_line_separator); - } - - content = first.toJava(indent, tracer); - buf.append(content); - if (first instanceof BasicBlockStatement && !content.isEmpty()) { - List exps = first.getExprents(); - if (exps.size() != 0) { - Exprent e = exps.get(exps.size() - 1); - if (!(e instanceof InvocationExprent - || e instanceof FunctionExprent - || (e instanceof AssignmentExprent && !(((AssignmentExprent) e).getLeft() instanceof VarExprent && ((VarExprent) ((AssignmentExprent) e).getLeft()).isDefinition())))) { - buf.append(new_line_separator); - } - } - } + @Override + public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { + TextBuffer buf = new TextBuffer(); + buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer)); + buf.append(first.toJava(indent, tracer)); if (isLabeled()) { - buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator); + buf.appendIndent(indent).append("label").append(this.id.toString()).append(":").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } - buf.append(indstr).append(headexprent.get(0).toJava(indent, tracer)).append(" {").append(new_line_separator); + buf.appendIndent(indent).append(headexprent.get(0).toJava(indent, tracer)).append(" {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); buf.append(ExprProcessor.jmpWrapper(body, indent + 1, true, tracer)); + + buf.appendIndent(indent).append("}").appendLineSeparator(); + mapMonitorExitInstr(tracer); tracer.incrementCurrentSourceLine(); - buf.append(indstr).append("}").append(new_line_separator); - tracer.incrementCurrentSourceLine(); - - return buf.toString(); + return buf; } + private void mapMonitorExitInstr(BytecodeMappingTracer tracer) { + BasicBlock block = body.getBasichead().getBlock(); + if (!block.getSeq().isEmpty() && block.getLastInstruction().opcode == CodeConstants.opc_monitorexit) { + Integer offset = block.getOldOffset(block.size() - 1); + if (offset > -1) tracer.addMapping(offset); + } + } + + @Override public void initExprents() { headexprent.set(0, first.getExprents().remove(first.getExprents().size() - 1)); } + @Override public List getSequentialObjects() { - List lst = new ArrayList(stats); + List lst = new ArrayList<>(stats); lst.add(1, headexprent.get(0)); return lst; } + @Override public void replaceExprent(Exprent oldexpr, Exprent newexpr) { if (headexprent.get(0) == oldexpr) { headexprent.set(0, newexpr); } } + @Override public void replaceStatement(Statement oldstat, Statement newstat) { if (body == oldstat) { @@ -147,10 +125,12 @@ public class SynchronizedStatement extends Statement { stats.removeWithKey(exc.id); } + @Override public Statement getSimpleCopy() { return new SynchronizedStatement(); } + @Override public void initSimpleCopy() { first = stats.get(0); body = stats.get(1); @@ -164,15 +144,7 @@ public class SynchronizedStatement extends Statement { return body; } - public void setBody(Statement body) { - this.body = body; - } - public List getHeadexprentList() { return headexprent; } - - public Exprent getHeadexprent() { - return headexprent.get(0); - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java index 08c32ec..f668aa7 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.vars; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; @@ -22,17 +8,15 @@ import java.util.ArrayList; import java.util.List; public class CheckTypesResult { - - private List lstMaxTypeExprents = new ArrayList(); - - private List lstMinTypeExprents = new ArrayList(); + private final List lstMaxTypeExprents = new ArrayList<>(); + private final List lstMinTypeExprents = new ArrayList<>(); public void addMaxTypeExprent(Exprent exprent, VarType type) { - lstMaxTypeExprents.add(new ExprentTypePair(exprent, type, null)); + lstMaxTypeExprents.add(new ExprentTypePair(exprent, type)); } public void addMinTypeExprent(Exprent exprent, VarType type) { - lstMinTypeExprents.add(new ExprentTypePair(exprent, type, null)); + lstMinTypeExprents.add(new ExprentTypePair(exprent, type)); } public List getLstMaxTypeExprents() { @@ -44,14 +28,12 @@ public class CheckTypesResult { } public static class ExprentTypePair { - public Exprent exprent; - public VarType type; - public VarType desttype; + public final Exprent exprent; + public final VarType type; - public ExprentTypePair(Exprent exprent, VarType type, VarType desttype) { + public ExprentTypePair(Exprent exprent, VarType type) { this.exprent = exprent; this.type = type; - this.desttype = desttype; } } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index fdd6cd5..1c13681 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.vars; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -34,24 +20,24 @@ import java.util.Map.Entry; public class VarDefinitionHelper { - private HashMap mapVarDefStatements; + private final HashMap mapVarDefStatements; // statement.id, defined vars - private HashMap> mapStatementVars; + private final HashMap> mapStatementVars; - private HashSet implDefVars; + private final HashSet implDefVars; - private VarProcessor varproc; + private final VarProcessor varproc; public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc) { - mapVarDefStatements = new HashMap(); - mapStatementVars = new HashMap>(); - implDefVars = new HashSet(); + mapVarDefStatements = new HashMap<>(); + mapStatementVars = new HashMap<>(); + implDefVars = new HashSet<>(); this.varproc = varproc; - VarNamesCollector vc = DecompilerContext.getVarNamesCollector(); + VarNamesCollector vc = varproc.getVarNamesCollector(); boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC); @@ -68,31 +54,33 @@ public class VarDefinitionHelper { int varindex = 0; for (int i = 0; i < paramcount; i++) { implDefVars.add(varindex); - varproc.setVarName(new VarVersionPaar(varindex, 0), vc.getFreeName(varindex)); + varproc.setVarName(new VarVersionPair(varindex, 0), "tbd"); if (thisvar) { if (i == 0) { varindex++; } else { - varindex += md.params[i - 1].stack_size; + varproc.setVarName(new VarVersionPair(varindex, 0), vc.getTypeLabel(md.params[i - 1], varindex)); + varindex += md.params[i - 1].stackSize; } } else { - varindex += md.params[i].stack_size; + varproc.setVarName(new VarVersionPair(varindex, 0), vc.getTypeLabel(md.params[i], varindex)); + varindex += md.params[i].stackSize; } } if (thisvar) { StructClass current_class = (StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS); - varproc.getThisvars().put(new VarVersionPaar(0, 0), current_class.qualifiedName); - varproc.setVarName(new VarVersionPaar(0, 0), "this"); + varproc.getThisVars().put(new VarVersionPair(0, 0), current_class.qualifiedName); + varproc.setVarName(new VarVersionPair(0, 0), "this"); vc.addName("this"); } // catch variables are implicitly defined - LinkedList stack = new LinkedList(); + LinkedList stack = new LinkedList<>(); stack.add(root); while (!stack.isEmpty()) { @@ -109,7 +97,7 @@ public class VarDefinitionHelper { if (lstVars != null) { for (VarExprent var : lstVars) { implDefVars.add(var.getIndex()); - varproc.setVarName(new VarVersionPaar(var), vc.getFreeName(var.getIndex())); + varproc.setVarName(new VarVersionPair(var), vc.getTypeLabel(var.getVarType(), var.getIndex())); var.setDefinition(true); } } @@ -122,8 +110,7 @@ public class VarDefinitionHelper { public void setVarDefinitions() { - - VarNamesCollector vc = DecompilerContext.getVarNamesCollector(); + VarNamesCollector vc = varproc.getVarNamesCollector(); for (Entry en : mapVarDefStatements.entrySet()) { Statement stat = en.getValue(); @@ -134,7 +121,7 @@ public class VarDefinitionHelper { continue; } - varproc.setVarName(new VarVersionPaar(index.intValue(), 0), vc.getFreeName(index)); + varproc.setVarName(new VarVersionPair(index.intValue(), 0), vc.getFreeName("local" + index)); // special case for if (stat.type == Statement.TYPE_DO) { @@ -147,7 +134,7 @@ public class VarDefinitionHelper { else { List lstSpecial = Arrays.asList(dstat.getConditionExprent(), dstat.getIncExprent()); for (VarExprent var : getAllVars(lstSpecial)) { - if (var.getIndex() == index.intValue()) { + if (var.getIndex() == index) { stat = stat.getParent(); break; } @@ -173,7 +160,7 @@ public class VarDefinitionHelper { boolean defset = false; - // search for the first assignement to var [index] + // search for the first assignment to var [index] int addindex = 0; for (Exprent expr : lst) { if (setDefinition(expr, index)) { @@ -196,11 +183,14 @@ public class VarDefinitionHelper { } if (!defset) { - VarExprent var = new VarExprent(index.intValue(), varproc.getVarType(new VarVersionPaar(index.intValue(), 0)), varproc); + VarExprent var = new VarExprent(index, varproc.getVarType(new VarVersionPair(index.intValue(), 0)), varproc); var.setDefinition(true); lst.add(addindex, var); } + + varproc.setVarName(new VarVersionPair(index.intValue(), 0), vc.getTypeLabel(varproc.getVarType(new VarVersionPair(index.intValue(), 0)), index)); + } } @@ -211,7 +201,7 @@ public class VarDefinitionHelper { private Statement findFirstBlock(Statement stat, Integer varindex) { - LinkedList stack = new LinkedList(); + LinkedList stack = new LinkedList<>(); stack.add(stat); while (!stack.isEmpty()) { @@ -251,15 +241,15 @@ public class VarDefinitionHelper { private Set initStatement(Statement stat) { - HashMap mapCount = new HashMap(); + HashMap mapCount = new HashMap<>(); List condlst; if (stat.getExprents() == null) { // recurse on children statements - List childVars = new ArrayList(); - List currVars = new ArrayList(); + List childVars = new ArrayList<>(); + List currVars = new ArrayList<>(); for (Object obj : stat.getSequentialObjects()) { if (obj instanceof Statement) { @@ -289,9 +279,9 @@ public class VarDefinitionHelper { for (Integer index : childVars) { Integer count = mapCount.get(index); if (count == null) { - count = new Integer(0); + count = 0; } - mapCount.put(index, new Integer(count.intValue() + 1)); + mapCount.put(index, count + 1); } condlst = getAllVars(currVars); @@ -302,15 +292,15 @@ public class VarDefinitionHelper { // this statement for (VarExprent var : condlst) { - mapCount.put(new Integer(var.getIndex()), new Integer(2)); + mapCount.put(var.getIndex(), 2); } - HashSet set = new HashSet(mapCount.keySet()); + HashSet set = new HashSet<>(mapCount.keySet()); // put all variables defined in this statement into the set for (Entry en : mapCount.entrySet()) { - if (en.getValue().intValue() > 1) { + if (en.getValue() > 1) { mapVarDefStatements.put(en.getKey(), stat); } } @@ -322,8 +312,8 @@ public class VarDefinitionHelper { private static List getAllVars(List lst) { - List res = new ArrayList(); - List listTemp = new ArrayList(); + List res = new ArrayList<>(); + List listTemp = new ArrayList<>(); for (Exprent expr : lst) { listTemp.addAll(expr.getAllExprents(true)); @@ -344,7 +334,7 @@ public class VarDefinitionHelper { Exprent left = ((AssignmentExprent)expr).getLeft(); if (left.type == Exprent.EXPRENT_VAR) { VarExprent var = (VarExprent)left; - if (var.getIndex() == index.intValue()) { + if (var.getIndex() == index) { var.setDefinition(true); return true; } 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 18314d8..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,142 +1,124 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; -import org.jetbrains.java.decompiler.util.VarHelper; // Spigot +import org.jetbrains.java.decompiler.util.TextUtil; import java.util.*; import java.util.Map.Entry; - public class VarProcessor { + private final VarNamesCollector varNamesCollector = new VarNamesCollector(); + private final StructMethod method; + private final MethodDescriptor methodDescriptor; + private Map mapVarNames = new HashMap<>(); + private VarVersionsProcessor varVersions; + private final Map thisVars = new HashMap<>(); + private final Set externalVars = new HashSet<>(); - private HashMap mapVarNames = new HashMap(); - - private VarVersionsProcessor varvers; - - private HashMap thisvars = new HashMap(); - - private HashSet externvars = new HashSet(); - - private VarHelper helper = new VarHelper(); // Spigot + public VarProcessor(StructMethod mt, MethodDescriptor md) { + method = mt; + methodDescriptor = md; + } public void setVarVersions(RootStatement root) { - - varvers = new VarVersionsProcessor(); - varvers.setVarVersions(root); + VarVersionsProcessor oldProcessor = varVersions; + varVersions = new VarVersionsProcessor(method, methodDescriptor); + varVersions.setVarVersions(root, oldProcessor); } public void setVarDefinitions(Statement root) { - mapVarNames = new HashMap(); - - VarDefinitionHelper defproc = new VarDefinitionHelper(root, - (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD), - this); - defproc.setVarDefinitions(); + mapVarNames = new HashMap<>(); + new VarDefinitionHelper(root, method, this).setVarDefinitions(); } public void setDebugVarNames(Map mapDebugVarNames) { - if (varvers == null) { + if (varVersions == null) { return; } - HashMap mapOriginalVarIndices = varvers.getMapOriginalVarIndices(); + Map mapOriginalVarIndices = varVersions.getMapOriginalVarIndices(); - List listVars = new ArrayList(mapVarNames.keySet()); - Collections.sort(listVars, new Comparator() { - public int compare(VarVersionPaar o1, VarVersionPaar o2) { - return o1.var > o2.var ? 1 : (o1.var == o2.var ? 0 : -1); - } - }); + List listVars = new ArrayList<>(mapVarNames.keySet()); + listVars.sort(Comparator.comparingInt(o -> o.var)); - HashMap mapNames = new HashMap(); + Map mapNames = new HashMap<>(); - for (VarVersionPaar varpaar : listVars) { - String name = mapVarNames.get(varpaar); + for (VarVersionPair pair : listVars) { + String name = mapVarNames.get(pair); - Integer orindex = mapOriginalVarIndices.get(varpaar.var); - if (orindex != null && mapDebugVarNames.containsKey(orindex)) { - name = mapDebugVarNames.get(orindex); + Integer index = mapOriginalVarIndices.get(pair.var); + if (index != null) { + String debugName = mapDebugVarNames.get(index); + if (debugName != null && TextUtil.isValidIdentifier(debugName, method.getBytecodeVersion())) { + name = debugName; + } } Integer counter = mapNames.get(name); - mapNames.put(name, counter == null ? counter = new Integer(0) : ++counter); + mapNames.put(name, counter == null ? counter = 0 : ++counter); if (counter > 0) { name += String.valueOf(counter); } - mapVarNames.put(varpaar, name); + mapVarNames.put(pair, name); } } - public void refreshVarNames(VarNamesCollector vc) { + public Integer getVarOriginalIndex(int index) { + return varVersions == null ? null : varVersions.getMapOriginalVarIndices().get(index); + } - HashMap tempVarNames = new HashMap(mapVarNames); - for (Entry ent : tempVarNames.entrySet()) { + public void refreshVarNames(VarNamesCollector vc) { + Map tempVarNames = new HashMap<>(mapVarNames); + for (Entry ent : tempVarNames.entrySet()) { mapVarNames.put(ent.getKey(), vc.getFreeName(ent.getValue())); } } - - public VarType getVarType(VarVersionPaar varpaar) { - return varvers == null ? null : varvers.getVarType(varpaar); + public VarNamesCollector getVarNamesCollector() { + return varNamesCollector; } - public void setVarType(VarVersionPaar varpaar, VarType type) { - varvers.setVarType(varpaar, type); + public VarType getVarType(VarVersionPair pair) { + return varVersions == null ? null : varVersions.getVarType(pair); } - public String getVarName(VarVersionPaar varpaar) { - // Spigot Start - String name = mapVarNames.get(varpaar); - if (name != null) { - mapVarNames.put(varpaar, name = helper.help(name, varpaar.type, varpaar.varargs)); - } - return name; - // Spigot End + public void setVarType(VarVersionPair pair, VarType type) { + varVersions.setVarType(pair, type); } - public void setVarName(VarVersionPaar varpaar, String name) { - mapVarNames.put(varpaar, name); + public String getVarName(VarVersionPair pair) { + return mapVarNames == null ? null : mapVarNames.get(pair); + } + + public void setVarName(VarVersionPair pair, String name) { + mapVarNames.put(pair, name); } public Collection getVarNames() { - return mapVarNames != null ? mapVarNames.values() : Collections.EMPTY_SET; + return mapVarNames != null ? mapVarNames.values() : Collections.emptySet(); } - public int getVarFinal(VarVersionPaar varpaar) { - return varvers == null ? VarTypeProcessor.VAR_FINAL : varvers.getVarFinal(varpaar); + public int getVarFinal(VarVersionPair pair) { + return varVersions == null ? VarTypeProcessor.VAR_FINAL : varVersions.getVarFinal(pair); } - public void setVarFinal(VarVersionPaar varpaar, int finaltype) { - varvers.setVarFinal(varpaar, finaltype); + public void setVarFinal(VarVersionPair pair, int finalType) { + varVersions.setVarFinal(pair, finalType); } - public HashMap getThisvars() { - return thisvars; + public Map getThisVars() { + return thisVars; } - public HashSet getExternvars() { - return externvars; + public Set getExternalVars() { + return externalVars; } } 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 a21d71f..c31cfad 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.vars; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -31,44 +17,54 @@ import org.jetbrains.java.decompiler.struct.gen.VarType; import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; public class VarTypeProcessor { - - public static final int VAR_NONFINAL = 1; - public static final int VAR_FINALEXPLICIT = 2; + public static final int VAR_NON_FINAL = 1; + public static final int VAR_EXPLICIT_FINAL = 2; public static final int VAR_FINAL = 3; - private HashMap mapExprentMinTypes = new HashMap(); + private final StructMethod method; + private final MethodDescriptor methodDescriptor; + private final Map mapExprentMinTypes = new HashMap<>(); + private final Map mapExprentMaxTypes = new HashMap<>(); + private final Map mapFinalVars = new HashMap<>(); - private HashMap mapExprentMaxTypes = new HashMap(); + public VarTypeProcessor(StructMethod mt, MethodDescriptor md) { + method = mt; + methodDescriptor = md; + } - private HashMap mapFinalVars = new HashMap(); + public void calculateVarTypes(RootStatement root, DirectGraph graph) { + setInitVars(root); + + resetExprentTypes(graph); + + //noinspection StatementWithEmptyBody + while (!processVarTypes(graph)) ; + } private void setInitVars(RootStatement root) { + boolean thisVar = !method.hasModifier(CodeConstants.ACC_STATIC); - StructMethod mt = (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD); + MethodDescriptor md = methodDescriptor; - // method descriptor - boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC); - - MethodDescriptor md = (MethodDescriptor)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR); - - if (thisvar) { - VarType cltype = new VarType(CodeConstants.TYPE_OBJECT, 0, - ((StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS)).qualifiedName); - mapExprentMinTypes.put(new VarVersionPaar(0, 1), cltype); - mapExprentMaxTypes.put(new VarVersionPaar(0, 1), cltype); + if (thisVar) { + StructClass cl = (StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS); + VarType clType = new VarType(CodeConstants.TYPE_OBJECT, 0, cl.qualifiedName); + mapExprentMinTypes.put(new VarVersionPair(0, 1), clType); + mapExprentMaxTypes.put(new VarVersionPair(0, 1), clType); } - int varindex = 0; + int varIndex = 0; for (int i = 0; i < md.params.length; i++) { - mapExprentMinTypes.put(new VarVersionPaar(varindex + (thisvar ? 1 : 0), 1), md.params[i]); - mapExprentMaxTypes.put(new VarVersionPaar(varindex + (thisvar ? 1 : 0), 1), md.params[i]); - varindex += md.params[i].stack_size; + mapExprentMinTypes.put(new VarVersionPair(varIndex + (thisVar ? 1 : 0), 1), md.params[i]); + mapExprentMaxTypes.put(new VarVersionPair(varIndex + (thisVar ? 1 : 0), 1), md.params[i]); + varIndex += md.params[i].stackSize; } // catch variables - LinkedList stack = new LinkedList(); + LinkedList stack = new LinkedList<>(); stack.add(root); while (!stack.isEmpty()) { @@ -84,8 +80,8 @@ public class VarTypeProcessor { if (lstVars != null) { for (VarExprent var : lstVars) { - mapExprentMinTypes.put(new VarVersionPaar(var.getIndex(), 1), var.getVartype()); - mapExprentMaxTypes.put(new VarVersionPaar(var.getIndex(), 1), var.getVartype()); + mapExprentMinTypes.put(new VarVersionPair(var.getIndex(), 1), var.getVarType()); + mapExprentMaxTypes.put(new VarVersionPair(var.getIndex(), 1), var.getVarType()); } } @@ -93,48 +89,30 @@ public class VarTypeProcessor { } } - public void calculateVarTypes(RootStatement root, DirectGraph dgraph) { + private static void resetExprentTypes(DirectGraph graph) { + graph.iterateExprents(exprent -> { + List lst = exprent.getAllExprents(true); + lst.add(exprent); - setInitVars(root); - - resetExprentTypes(dgraph); - - while (!processVarTypes(dgraph)) ; - } - - private static void resetExprentTypes(DirectGraph dgraph) { - - dgraph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - List lst = exprent.getAllExprents(true); - lst.add(exprent); - - for (Exprent expr : lst) { - if (expr.type == Exprent.EXPRENT_VAR) { - ((VarExprent)expr).setVartype(VarType.VARTYPE_UNKNOWN); - } - else if (expr.type == Exprent.EXPRENT_CONST) { - ConstExprent cexpr = (ConstExprent)expr; - if (cexpr.getConsttype().type_family == CodeConstants.TYPE_FAMILY_INTEGER) { - cexpr.setConsttype(new ConstExprent(cexpr.getIntValue(), cexpr.isBoolPermitted()).getConsttype()); - } + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR) { + ((VarExprent)expr).setVarType(VarType.VARTYPE_UNKNOWN); + } + else if (expr.type == Exprent.EXPRENT_CONST) { + ConstExprent constExpr = (ConstExprent)expr; + if (constExpr.getConstType().typeFamily == CodeConstants.TYPE_FAMILY_INTEGER) { + constExpr.setConstType(new ConstExprent(constExpr.getIntValue(), constExpr.isBoolPermitted(), null).getConstType()); } } - return 0; } + return 0; }); } - private boolean processVarTypes(DirectGraph dgraph) { - - return dgraph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - return checkTypeExprent(exprent) ? 0 : 1; - } - }); + private boolean processVarTypes(DirectGraph graph) { + return graph.iterateExprents(exprent -> checkTypeExprent(exprent) ? 0 : 1); } - private boolean checkTypeExprent(Exprent exprent) { for (Exprent expr : exprent.getAllExprents()) { @@ -144,135 +122,138 @@ public class VarTypeProcessor { } if (exprent.type == Exprent.EXPRENT_CONST) { - ConstExprent cexpr = (ConstExprent)exprent; - if (cexpr.getConsttype().type_family <= CodeConstants.TYPE_FAMILY_INTEGER) { // boolean or integer - VarVersionPaar cpaar = new VarVersionPaar(cexpr.id, -1); - if (!mapExprentMinTypes.containsKey(cpaar)) { - mapExprentMinTypes.put(cpaar, cexpr.getConsttype()); + ConstExprent constExpr = (ConstExprent)exprent; + if (constExpr.getConstType().typeFamily <= CodeConstants.TYPE_FAMILY_INTEGER) { // boolean or integer + VarVersionPair pair = new VarVersionPair(constExpr.id, -1); + if (!mapExprentMinTypes.containsKey(pair)) { + mapExprentMinTypes.put(pair, constExpr.getConstType()); } } } CheckTypesResult result = exprent.checkExprTypeBounds(); - for (CheckTypesResult.ExprentTypePair entry : result.getLstMaxTypeExprents()) { - if (entry.type.type_family != CodeConstants.TYPE_FAMILY_OBJECT) { - changeExprentType(entry.exprent, entry.type, 1); - } - } - boolean res = true; - for (CheckTypesResult.ExprentTypePair entry : result.getLstMinTypeExprents()) { - res &= changeExprentType(entry.exprent, entry.type, 0); + if (result != null) { + for (CheckTypesResult.ExprentTypePair entry : result.getLstMaxTypeExprents()) { + if (entry.type.typeFamily != CodeConstants.TYPE_FAMILY_OBJECT) { + changeExprentType(entry.exprent, entry.type, 1); + } + } + + for (CheckTypesResult.ExprentTypePair entry : result.getLstMinTypeExprents()) { + res &= changeExprentType(entry.exprent, entry.type, 0); + } } return res; } - private boolean changeExprentType(Exprent exprent, VarType newtype, int minmax) { - + private boolean changeExprentType(Exprent exprent, VarType newType, int minMax) { boolean res = true; switch (exprent.type) { case Exprent.EXPRENT_CONST: - ConstExprent cexpr = (ConstExprent)exprent; - VarType consttype = cexpr.getConsttype(); + ConstExprent constExpr = (ConstExprent)exprent; + VarType constType = constExpr.getConstType(); - if (newtype.type_family > CodeConstants.TYPE_FAMILY_INTEGER || consttype.type_family > CodeConstants.TYPE_FAMILY_INTEGER) { + if (newType.typeFamily > CodeConstants.TYPE_FAMILY_INTEGER || constType.typeFamily > CodeConstants.TYPE_FAMILY_INTEGER) { return true; } - else if (newtype.type_family == CodeConstants.TYPE_FAMILY_INTEGER) { - VarType mininteger = new ConstExprent((Integer)((ConstExprent)exprent).getValue(), false).getConsttype(); - if (mininteger.isStrictSuperset(newtype)) { - newtype = mininteger; + else if (newType.typeFamily == CodeConstants.TYPE_FAMILY_INTEGER) { + VarType minInteger = new ConstExprent((Integer)constExpr.getValue(), false, null).getConstType(); + if (minInteger.isStrictSuperset(newType)) { + newType = minInteger; } } case Exprent.EXPRENT_VAR: - VarVersionPaar varpaar = null; + VarVersionPair pair = null; if (exprent.type == Exprent.EXPRENT_CONST) { - varpaar = new VarVersionPaar(((ConstExprent)exprent).id, -1); + pair = new VarVersionPair(exprent.id, -1); } else if (exprent.type == Exprent.EXPRENT_VAR) { - varpaar = new VarVersionPaar((VarExprent)exprent); + pair = new VarVersionPair((VarExprent)exprent); } - if (minmax == 0) { // min - VarType currentMinType = mapExprentMinTypes.get(varpaar); + if (minMax == 0) { // min + VarType currentMinType = mapExprentMinTypes.get(pair); VarType newMinType; - if (currentMinType == null || newtype.type_family > currentMinType.type_family) { - newMinType = newtype; + if (currentMinType == null || newType.typeFamily > currentMinType.typeFamily) { + newMinType = newType; } - else if (newtype.type_family < currentMinType.type_family) { + else if (newType.typeFamily < currentMinType.typeFamily) { return true; } else { - newMinType = VarType.getCommonSupertype(currentMinType, newtype); + newMinType = VarType.getCommonSupertype(currentMinType, newType); } - mapExprentMinTypes.put(varpaar, newMinType); + mapExprentMinTypes.put(pair, newMinType); if (exprent.type == Exprent.EXPRENT_CONST) { - ((ConstExprent)exprent).setConsttype(newMinType); + ((ConstExprent)exprent).setConstType(newMinType); } - if (currentMinType != null && (newMinType.type_family > currentMinType.type_family || - newMinType.isStrictSuperset(currentMinType))) { + if (currentMinType != null && (newMinType.typeFamily > currentMinType.typeFamily || newMinType.isStrictSuperset(currentMinType))) { return false; } } else { // max - VarType currentMaxType = mapExprentMaxTypes.get(varpaar); + VarType currentMaxType = mapExprentMaxTypes.get(pair); VarType newMaxType; - if (currentMaxType == null || newtype.type_family < currentMaxType.type_family) { - newMaxType = newtype; + if (currentMaxType == null || newType.typeFamily < currentMaxType.typeFamily) { + newMaxType = newType; } - else if (newtype.type_family > currentMaxType.type_family) { + else if (newType.typeFamily > currentMaxType.typeFamily) { return true; } else { - newMaxType = VarType.getCommonMinType(currentMaxType, newtype); + newMaxType = VarType.getCommonMinType(currentMaxType, newType); } - mapExprentMaxTypes.put(varpaar, newMaxType); + mapExprentMaxTypes.put(pair, newMaxType); } break; + case Exprent.EXPRENT_ASSIGNMENT: - return changeExprentType(((AssignmentExprent)exprent).getRight(), newtype, minmax); + return changeExprentType(((AssignmentExprent)exprent).getRight(), newType, minMax); + case Exprent.EXPRENT_FUNCTION: FunctionExprent func = (FunctionExprent)exprent; - switch (func.getFunctype()) { + switch (func.getFuncType()) { case FunctionExprent.FUNCTION_IIF: // FIXME: - res &= changeExprentType(func.getLstOperands().get(1), newtype, minmax); - res &= changeExprentType(func.getLstOperands().get(2), newtype, minmax); + res = changeExprentType(func.getLstOperands().get(1), newType, minMax) & + changeExprentType(func.getLstOperands().get(2), newType, minMax); break; case FunctionExprent.FUNCTION_AND: case FunctionExprent.FUNCTION_OR: case FunctionExprent.FUNCTION_XOR: - res &= changeExprentType(func.getLstOperands().get(0), newtype, minmax); - res &= changeExprentType(func.getLstOperands().get(1), newtype, minmax); + res = changeExprentType(func.getLstOperands().get(0), newType, minMax) & + changeExprentType(func.getLstOperands().get(1), newType, minMax); + break; } } return res; } - public HashMap getMapExprentMaxTypes() { + public Map getMapExprentMaxTypes() { return mapExprentMaxTypes; } - public HashMap getMapExprentMinTypes() { + public Map getMapExprentMinTypes() { return mapExprentMinTypes; } - public HashMap getMapFinalVars() { + public Map getMapFinalVars() { return mapFinalVars; } - public void setVarType(VarVersionPaar varpaar, VarType type) { - mapExprentMinTypes.put(varpaar, type); + public void setVarType(VarVersionPair pair, VarType type) { + mapExprentMinTypes.put(pair, type); } - public VarType getVarType(VarVersionPaar varpaar) { - return mapExprentMinTypes.get(varpaar); + public VarType getVarType(VarVersionPair pair) { + return mapExprentMinTypes.get(pair); } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java index 9d97982..2e67a88 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java @@ -1,32 +1,18 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.vars; -public class VarVersionEdge { // FIXME: can be removed? +public class VarVersionEdge { // FIXME: can be removed? public static final int EDGE_GENERAL = 0; public static final int EDGE_PHANTOM = 1; - public int type; + public final int type; - public VarVersionNode source; + public final VarVersionNode source; - public VarVersionNode dest; + public final VarVersionNode dest; - private int hashCode; + private final int hashCode; public VarVersionEdge(int type, VarVersionNode source, VarVersionNode dest) { this.type = type; @@ -38,7 +24,7 @@ public class VarVersionEdge { // FIXME: can be removed? @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof VarVersionEdge)) return false; + if (!(o instanceof VarVersionEdge)) return false; VarVersionEdge edge = (VarVersionEdge)o; return type == edge.type && source == edge.source && dest == edge.dest; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java index 587c653..914995e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.vars; import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode; @@ -27,13 +13,13 @@ public class VarVersionNode implements IGraphNode { public static final int FLAG_PHANTOM_FINEXIT = 2; - public int var; + public final int var; - public int version; + public final int version; - public Set succs = new HashSet(); + public final Set succs = new HashSet<>(); - public Set preds = new HashSet(); + public final Set preds = new HashSet<>(); public int flags; @@ -45,12 +31,9 @@ public class VarVersionNode implements IGraphNode { this.version = version; } - public VarVersionPaar getVarPaar() { - return new VarVersionPaar(var, version); - } - + @Override public List getPredecessors() { - List lst = new ArrayList(preds.size()); + List lst = new ArrayList<>(preds.size()); for (VarVersionEdge edge : preds) { lst.add(edge.source); } @@ -77,4 +60,4 @@ public class VarVersionNode implements IGraphNode { public String toString() { return "(" + var + "_" + version + ")"; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java deleted file mode 100644 index 3bf29fe..0000000 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.modules.decompiler.vars; - -import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; - -public class VarVersionPaar implements Comparable { // Spigot - - public int var; - public int version; - public String type; - public boolean varargs; - - private int hashCode = -1; - - public VarVersionPaar(int var, int version) { - this.var = var; - this.version = version; - } - - public VarVersionPaar(int var, int version, String type, boolean varargs) { - this.var = var; - this.version = version; - this.type = type; - this.varargs = varargs; - } - - public VarVersionPaar(VarExprent var) { - this.var = var.getIndex(); - this.version = var.getVersion(); - } - - @Override - public boolean equals(Object o) { - if (o == this) return true; - if (o == null || !(o instanceof VarVersionPaar)) return false; - - VarVersionPaar paar = (VarVersionPaar)o; - return var == paar.var && version == paar.version; - } - - @Override - public int hashCode() { - if (hashCode == -1) { - hashCode = this.var * 3 + this.version; - } - return hashCode; - } - - @Override - public String toString() { - return "(" + var + "," + version + ")"; - } - - // Spigot Start - @Override - public int compareTo(VarVersionPaar o) { - if (this.var != o.var) { - return this.var - o.var; - } - return this.version - o.version; - } - // Spigot End -} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPair.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPair.java new file mode 100644 index 0000000..8554988 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPair.java @@ -0,0 +1,49 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.modules.decompiler.vars; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; + +public class VarVersionPair { + + public final int var; + public final int version; + + private int hashCode = -1; + + public VarVersionPair(int var, int version) { + this.var = var; + this.version = version; + } + + public VarVersionPair(Integer var, Integer version) { + this.var = var; + this.version = version; + } + + public VarVersionPair(VarExprent var) { + this.var = var.getIndex(); + this.version = var.getVersion(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof VarVersionPair)) return false; + + VarVersionPair paar = (VarVersionPair)o; + return var == paar.var && version == paar.version; + } + + @Override + public int hashCode() { + if (hashCode == -1) { + hashCode = this.var * 3 + this.version; + } + return hashCode; + } + + @Override + public String toString() { + return "(" + var + "," + version + ")"; + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java index f839a05..58eda68 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.vars; import org.jetbrains.java.decompiler.modules.decompiler.decompose.GenericDominatorEngine; @@ -22,43 +8,36 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.util.*; - public class VarVersionsGraph { - - public int counter = 0; - - public VBStyleCollection nodes = new VBStyleCollection(); + public final VBStyleCollection nodes = new VBStyleCollection<>(); private GenericDominatorEngine engine; - public VarVersionNode createNode(VarVersionPaar ver) { + public VarVersionNode createNode(VarVersionPair ver) { VarVersionNode node; nodes.addWithKey(node = new VarVersionNode(ver.var, ver.version), ver); return node; } - public void addNodes(Collection colnodes, Collection colpaars) { + public void addNodes(Collection colnodes, Collection colpaars) { nodes.addAllWithKey(colnodes, colpaars); } - public boolean isDominatorSet(VarVersionNode node, HashSet domnodes) { - + public boolean isDominatorSet(VarVersionNode node, Set domnodes) { if (domnodes.size() == 1) { return engine.isDominator(node, domnodes.iterator().next()); } else { - - HashSet marked = new HashSet(); + Set marked = new HashSet<>(); if (domnodes.contains(node)) { return true; } - LinkedList lstNodes = new LinkedList(); + List lstNodes = new LinkedList<>(); lstNodes.add(node); while (!lstNodes.isEmpty()) { - VarVersionNode nd = lstNodes.remove(0); if (marked.contains(nd)) { continue; @@ -84,8 +63,7 @@ public class VarVersionsGraph { } public void initDominators() { - - final HashSet roots = new HashSet(); + Set roots = new HashSet<>(); for (VarVersionNode node : nodes) { if (node.preds.isEmpty()) { @@ -94,10 +72,12 @@ public class VarVersionsGraph { } engine = new GenericDominatorEngine(new IGraph() { + @Override public List getReversePostOrderList() { return getReversedPostOrder(roots); } + @Override public Set getRoots() { return new HashSet(roots); } @@ -106,62 +86,49 @@ public class VarVersionsGraph { engine.initialize(); } - private static LinkedList getReversedPostOrder(Collection roots) { - - LinkedList lst = new LinkedList(); - HashSet setVisited = new HashSet(); + private static List getReversedPostOrder(Collection roots) { + List lst = new LinkedList<>(); + Set setVisited = new HashSet<>(); for (VarVersionNode root : roots) { - - LinkedList lstTemp = new LinkedList(); + List lstTemp = new LinkedList<>(); addToReversePostOrderListIterative(root, lstTemp, setVisited); - lst.addAll(lstTemp); } return lst; } - private static void addToReversePostOrderListIterative(VarVersionNode root, List lst, HashSet setVisited) { - - HashMap> mapNodeSuccs = new HashMap>(); - - LinkedList stackNode = new LinkedList(); - LinkedList stackIndex = new LinkedList(); + private static void addToReversePostOrderListIterative(VarVersionNode root, List lst, Set setVisited) { + Map> mapNodeSuccs = new HashMap<>(); + LinkedList stackNode = new LinkedList<>(); + LinkedList stackIndex = new LinkedList<>(); stackNode.add(root); stackIndex.add(0); while (!stackNode.isEmpty()) { - VarVersionNode node = stackNode.getLast(); int index = stackIndex.removeLast(); setVisited.add(node); - List lstSuccs = mapNodeSuccs.get(node); - if (lstSuccs == null) { - mapNodeSuccs.put(node, lstSuccs = new ArrayList(node.succs)); - } - + List lstSuccs = mapNodeSuccs.computeIfAbsent(node, n -> new ArrayList<>(n.succs)); for (; index < lstSuccs.size(); index++) { VarVersionNode succ = lstSuccs.get(index).dest; if (!setVisited.contains(succ)) { stackIndex.add(index + 1); - stackNode.add(succ); stackIndex.add(0); - break; } } if (index == lstSuccs.size()) { lst.add(0, node); - stackNode.removeLast(); } } } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java index 6a30f1b..adbb6fd 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.decompiler.vars; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -26,61 +12,56 @@ import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatements import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; import java.util.*; import java.util.Map.Entry; -import org.jetbrains.java.decompiler.util.SortUtil; public class VarVersionsProcessor { + private final StructMethod method; + private Map mapOriginalVarIndices = Collections.emptyMap(); + private final VarTypeProcessor typeProcessor; - private HashMap mapOriginalVarIndices = new HashMap(); - - private VarTypeProcessor typeproc; - - public void setVarVersions(RootStatement root) { - - StructMethod mt = (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD); + public VarVersionsProcessor(StructMethod mt, MethodDescriptor md) { + method = mt; + typeProcessor = new VarTypeProcessor(mt, md); + } + public void setVarVersions(RootStatement root, VarVersionsProcessor previousVersionsProcessor) { SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); - ssa.splitVariables(root, mt); + ssa.splitVariables(root, method); - FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); - DirectGraph dgraph = flatthelper.buildDirectGraph(root); + FlattenStatementsHelper flattenHelper = new FlattenStatementsHelper(); + DirectGraph graph = flattenHelper.buildDirectGraph(root); - // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + mergePhiVersions(ssa, graph); - mergePhiVersions(ssa, dgraph); + typeProcessor.calculateVarTypes(root, graph); - // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); - - typeproc = new VarTypeProcessor(); - typeproc.calculateVarTypes(root, dgraph); - - simpleMerge(typeproc, dgraph, mt); + simpleMerge(typeProcessor, graph, method); // FIXME: advanced merging - eliminateNonJavaTypes(typeproc); + eliminateNonJavaTypes(typeProcessor); - setNewVarIndices(typeproc, dgraph); + setNewVarIndices(typeProcessor, graph, previousVersionsProcessor); } - private static void mergePhiVersions(SSAConstructorSparseEx ssa, DirectGraph dgraph) { - + private static void mergePhiVersions(SSAConstructorSparseEx ssa, DirectGraph graph) { // collect phi versions - List> lst = new ArrayList>(); - for (Entry> ent : ssa.getPhi().entrySet()) { - HashSet set = new HashSet(); + List> lst = new ArrayList<>(); + for (Entry> ent : ssa.getPhi().entrySet()) { + Set set = new HashSet<>(); set.add(ent.getKey()); - for (Integer vers : ent.getValue()) { - set.add(new VarVersionPaar(ent.getKey().var, vers.intValue())); + for (Integer version : ent.getValue()) { + set.add(new VarVersionPair(ent.getKey().var, version.intValue())); } for (int i = lst.size() - 1; i >= 0; i--) { - HashSet tset = lst.get(i); - HashSet intersection = new HashSet(set); + Set tset = lst.get(i); + Set intersection = new HashSet<>(set); intersection.retainAll(tset); if (!intersection.isEmpty()) { @@ -92,59 +73,59 @@ public class VarVersionsProcessor { lst.add(set); } - final HashMap phivers = new HashMap(); - for (HashSet set : lst) { + Map phiVersions = new HashMap<>(); + for (Set set : lst) { int min = Integer.MAX_VALUE; - for (VarVersionPaar paar : set) { + for (VarVersionPair paar : set) { if (paar.version < min) { min = paar.version; } } - for (VarVersionPaar paar : set) { - phivers.put(new VarVersionPaar(paar.var, paar.version), min); + for (VarVersionPair paar : set) { + phiVersions.put(new VarVersionPair(paar.var, paar.version), min); } } + updateVersions(graph, phiVersions); + } - dgraph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - List lst = exprent.getAllExprents(true); - lst.add(exprent); + private static void updateVersions(DirectGraph graph, final Map versions) { + graph.iterateExprents(exprent -> { + List lst = exprent.getAllExprents(true); + lst.add(exprent); - for (Exprent expr : lst) { - if (expr.type == Exprent.EXPRENT_VAR) { - VarExprent var = (VarExprent)expr; - Integer vers = phivers.get(new VarVersionPaar(var)); - if (vers != null) { - var.setVersion(vers); - } + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)expr; + Integer version = versions.get(new VarVersionPair(var)); + if (version != null) { + var.setVersion(version); } } - return 0; } + + return 0; }); } - private static void eliminateNonJavaTypes(VarTypeProcessor typeproc) { + private static void eliminateNonJavaTypes(VarTypeProcessor typeProcessor) { + Map mapExprentMaxTypes = typeProcessor.getMapExprentMaxTypes(); + Map mapExprentMinTypes = typeProcessor.getMapExprentMinTypes(); - HashMap mapExprentMaxTypes = typeproc.getMapExprentMaxTypes(); - HashMap mapExprentMinTypes = typeproc.getMapExprentMinTypes(); - - HashSet set = new HashSet(mapExprentMinTypes.keySet()); - for (VarVersionPaar paar : set) { + for (VarVersionPair paar : new ArrayList<>(mapExprentMinTypes.keySet())) { VarType type = mapExprentMinTypes.get(paar); - VarType maxtype = mapExprentMaxTypes.get(paar); + VarType maxType = mapExprentMaxTypes.get(paar); if (type.type == CodeConstants.TYPE_BYTECHAR || type.type == CodeConstants.TYPE_SHORTCHAR) { - if (maxtype != null && maxtype.type == CodeConstants.TYPE_CHAR) { + if (maxType != null && maxType.type == CodeConstants.TYPE_CHAR) { type = VarType.VARTYPE_CHAR; } else { type = type.type == CodeConstants.TYPE_BYTECHAR ? VarType.VARTYPE_BYTE : VarType.VARTYPE_SHORT; } mapExprentMinTypes.put(paar, type); - //} else if(type.type == CodeConstants.TYPE_CHAR && (maxtype == null || maxtype.type == CodeConstants.TYPE_INT)) { // when possible, lift char to int + //} else if(type.type == CodeConstants.TYPE_CHAR && (maxType == null || maxType.type == CodeConstants.TYPE_INT)) { // when possible, lift char to int // mapExprentMinTypes.put(paar, VarType.VARTYPE_INT); } else if (type.type == CodeConstants.TYPE_NULL) { @@ -153,67 +134,64 @@ public class VarVersionsProcessor { } } - private static void simpleMerge(VarTypeProcessor typeproc, DirectGraph dgraph, StructMethod mt) { + private static void simpleMerge(VarTypeProcessor typeProcessor, DirectGraph graph, StructMethod mt) { + Map mapExprentMaxTypes = typeProcessor.getMapExprentMaxTypes(); + Map mapExprentMinTypes = typeProcessor.getMapExprentMinTypes(); - HashMap mapExprentMaxTypes = typeproc.getMapExprentMaxTypes(); - HashMap mapExprentMinTypes = typeproc.getMapExprentMinTypes(); + Map> mapVarVersions = new HashMap<>(); - HashMap> mapVarVersions = new HashMap>(); - - for (VarVersionPaar varpaar : mapExprentMinTypes.keySet()) { - if (varpaar.version >= 0) { // don't merge constants - HashSet set = mapVarVersions.get(varpaar.var); - if (set == null) { - set = new HashSet(); - mapVarVersions.put(varpaar.var, set); - } - set.add(varpaar.version); + for (VarVersionPair pair : mapExprentMinTypes.keySet()) { + if (pair.version >= 0) { // don't merge constants + mapVarVersions.computeIfAbsent(pair.var, k -> new HashSet<>()).add(pair.version); } } boolean is_method_static = mt.hasModifier(CodeConstants.ACC_STATIC); - final HashMap mapMergedVersions = new HashMap(); + Map mapMergedVersions = new HashMap<>(); - for (Entry> ent : mapVarVersions.entrySet()) { + for (Entry> ent : mapVarVersions.entrySet()) { if (ent.getValue().size() > 1) { - List lstVersions = new ArrayList(ent.getValue()); + List lstVersions = new ArrayList<>(ent.getValue()); Collections.sort(lstVersions); for (int i = 0; i < lstVersions.size(); i++) { - VarVersionPaar firstpaar = new VarVersionPaar(ent.getKey(), lstVersions.get(i)); - VarType firsttype = mapExprentMinTypes.get(firstpaar); + VarVersionPair firstPair = new VarVersionPair(ent.getKey(), lstVersions.get(i)); + VarType firstType = mapExprentMinTypes.get(firstPair); - if (firstpaar.var == 0 && firstpaar.version == 1 && !is_method_static) { + if (firstPair.var == 0 && firstPair.version == 1 && !is_method_static) { continue; // don't merge 'this' variable } for (int j = i + 1; j < lstVersions.size(); j++) { - VarVersionPaar secpaar = new VarVersionPaar(ent.getKey(), lstVersions.get(j)); - VarType sectype = mapExprentMinTypes.get(secpaar); + VarVersionPair secondPair = new VarVersionPair(ent.getKey(), lstVersions.get(j)); + VarType secondType = mapExprentMinTypes.get(secondPair); - if (firsttype.equals(sectype) || (firsttype.equals(VarType.VARTYPE_NULL) && sectype.type == CodeConstants.TYPE_OBJECT) - || (sectype.equals(VarType.VARTYPE_NULL) && firsttype.type == CodeConstants.TYPE_OBJECT)) { + if (firstType.equals(secondType) || + (firstType.equals(VarType.VARTYPE_NULL) && secondType.type == CodeConstants.TYPE_OBJECT) || + (secondType.equals(VarType.VARTYPE_NULL) && firstType.type == CodeConstants.TYPE_OBJECT)) { - VarType firstMaxType = mapExprentMaxTypes.get(firstpaar); - VarType secMaxType = mapExprentMaxTypes.get(secpaar); - mapExprentMaxTypes.put(firstpaar, firstMaxType == null ? secMaxType : - (secMaxType == null ? firstMaxType : VarType.getCommonMinType(firstMaxType, secMaxType))); + VarType firstMaxType = mapExprentMaxTypes.get(firstPair); + VarType secondMaxType = mapExprentMaxTypes.get(secondPair); + VarType type = firstMaxType == null ? secondMaxType : + secondMaxType == null ? firstMaxType : + VarType.getCommonMinType(firstMaxType, secondMaxType); + mapExprentMaxTypes.put(firstPair, type); + mapMergedVersions.put(secondPair, firstPair.version); + mapExprentMaxTypes.remove(secondPair); + mapExprentMinTypes.remove(secondPair); - mapMergedVersions.put(secpaar, firstpaar.version); - mapExprentMaxTypes.remove(secpaar); - mapExprentMinTypes.remove(secpaar); - - if (firsttype.equals(VarType.VARTYPE_NULL)) { - mapExprentMinTypes.put(firstpaar, sectype); - firsttype = sectype; + if (firstType.equals(VarType.VARTYPE_NULL)) { + mapExprentMinTypes.put(firstPair, secondType); + firstType = secondType; } - typeproc.getMapFinalVars().put(firstpaar, VarTypeProcessor.VAR_NONFINAL); + typeProcessor.getMapFinalVars().put(firstPair, VarTypeProcessor.VAR_NON_FINAL); lstVersions.remove(j); + //noinspection AssignmentToForLoopParameter j--; } } @@ -222,115 +200,98 @@ public class VarVersionsProcessor { } if (!mapMergedVersions.isEmpty()) { - dgraph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - List lst = exprent.getAllExprents(true); - lst.add(exprent); - - for (Exprent expr : lst) { - if (expr.type == Exprent.EXPRENT_VAR) { - VarExprent varex = (VarExprent)expr; - Integer newversion = mapMergedVersions.get(new VarVersionPaar(varex)); - if (newversion != null) { - varex.setVersion(newversion); - } - } - } - - return 0; - } - }); + updateVersions(graph, mapMergedVersions); } } - private void setNewVarIndices(VarTypeProcessor typeproc, DirectGraph dgraph) { + private void setNewVarIndices(VarTypeProcessor typeProcessor, DirectGraph graph, VarVersionsProcessor previousVersionsProcessor) { + final Map mapExprentMaxTypes = typeProcessor.getMapExprentMaxTypes(); + Map mapExprentMinTypes = typeProcessor.getMapExprentMinTypes(); + Map mapFinalVars = typeProcessor.getMapFinalVars(); - final HashMap mapExprentMaxTypes = typeproc.getMapExprentMaxTypes(); - HashMap mapExprentMinTypes = typeproc.getMapExprentMinTypes(); - HashMap mapFinalVars = typeproc.getMapFinalVars(); + CounterContainer counters = DecompilerContext.getCounterContainer(); - CounterContainer ccon = DecompilerContext.getCounterContainer(); + final Map mapVarPaar = new HashMap<>(); + Map mapOriginalVarIndices = new HashMap<>(); - final HashMap mapVarPaar = new HashMap(); - HashMap mapOriginalVarIndices = new HashMap(); + // map var-version pairs on new var indexes + for (VarVersionPair pair : new ArrayList<>(mapExprentMinTypes.keySet())) { - // map var-version paars on new var indexes - // Spigot Start - for (Iterator iter = SortUtil.sortComparable(mapExprentMinTypes.keySet().iterator()); iter.hasNext();) { - VarVersionPaar vpaar = iter.next(); - // Spigot End - if (vpaar.version >= 0) { - int newindex = vpaar.version == 1 ? vpaar.var : - ccon.getCounterAndIncrement(CounterContainer.VAR_COUNTER); + if (pair.version >= 0) { + int newIndex = pair.version == 1 ? pair.var : counters.getCounterAndIncrement(CounterContainer.VAR_COUNTER); - VarVersionPaar newvar = new VarVersionPaar(newindex, 0); + VarVersionPair newVar = new VarVersionPair(newIndex, 0); - mapExprentMinTypes.put(newvar, mapExprentMinTypes.get(vpaar)); - mapExprentMaxTypes.put(newvar, mapExprentMaxTypes.get(vpaar)); + mapExprentMinTypes.put(newVar, mapExprentMinTypes.get(pair)); + mapExprentMaxTypes.put(newVar, mapExprentMaxTypes.get(pair)); - if (mapFinalVars.containsKey(vpaar)) { - mapFinalVars.put(newvar, mapFinalVars.remove(vpaar)); + if (mapFinalVars.containsKey(pair)) { + mapFinalVars.put(newVar, mapFinalVars.remove(pair)); } - mapVarPaar.put(vpaar, newindex); - mapOriginalVarIndices.put(newindex, vpaar.var); + mapVarPaar.put(pair, newIndex); + mapOriginalVarIndices.put(newIndex, pair.var); } } // set new vars - dgraph.iterateExprents(new DirectGraph.ExprentIterator() { - public int processExprent(Exprent exprent) { - List lst = exprent.getAllExprents(true); - lst.add(exprent); + graph.iterateExprents(exprent -> { + List lst = exprent.getAllExprents(true); + lst.add(exprent); - for (Exprent expr : lst) { - if (expr.type == Exprent.EXPRENT_VAR) { - VarExprent varex = (VarExprent)expr; - Integer newvarindex = mapVarPaar.get(new VarVersionPaar(varex)); - if (newvarindex != null) { - varex.setIndex(newvarindex); - varex.setVersion(0); - } - } - else if (expr.type == Exprent.EXPRENT_CONST) { - VarType maxType = mapExprentMaxTypes.get(new VarVersionPaar(expr.id, -1)); - if (maxType != null && maxType.equals(VarType.VARTYPE_CHAR)) { - ((ConstExprent)expr).setConsttype(maxType); - } + for (Exprent expr : lst) { + if (expr.type == Exprent.EXPRENT_VAR) { + VarExprent newVar = (VarExprent)expr; + Integer newVarIndex = mapVarPaar.get(new VarVersionPair(newVar)); + if (newVarIndex != null) { + newVar.setIndex(newVarIndex); + newVar.setVersion(0); + } + } + else if (expr.type == Exprent.EXPRENT_CONST) { + VarType maxType = mapExprentMaxTypes.get(new VarVersionPair(expr.id, -1)); + if (maxType != null && maxType.equals(VarType.VARTYPE_CHAR)) { + ((ConstExprent)expr).setConstType(maxType); } } - - return 0; } + + return 0; }); - this.mapOriginalVarIndices = mapOriginalVarIndices; - } - - public VarType getVarType(VarVersionPaar varpaar) { - return typeproc == null ? null : typeproc.getVarType(varpaar); - } - - public void setVarType(VarVersionPaar varpaar, VarType type) { - typeproc.setVarType(varpaar, type); - } - - public int getVarFinal(VarVersionPaar varpaar) { - - int ret = VarTypeProcessor.VAR_FINAL; - if (typeproc != null) { - Integer fin = typeproc.getMapFinalVars().get(varpaar); - ret = fin == null ? VarTypeProcessor.VAR_FINAL : fin.intValue(); + if (previousVersionsProcessor != null) { + Map oldIndices = previousVersionsProcessor.getMapOriginalVarIndices(); + this.mapOriginalVarIndices = new HashMap<>(mapOriginalVarIndices.size()); + for (Entry entry : mapOriginalVarIndices.entrySet()) { + Integer value = entry.getValue(); + Integer oldValue = oldIndices.get(value); + value = oldValue != null ? oldValue : value; + this.mapOriginalVarIndices.put(entry.getKey(), value); + } + } + else { + this.mapOriginalVarIndices = mapOriginalVarIndices; } - - return ret; } - public void setVarFinal(VarVersionPaar varpaar, int finaltype) { - typeproc.getMapFinalVars().put(varpaar, finaltype); + public VarType getVarType(VarVersionPair pair) { + return typeProcessor.getVarType(pair); } - public HashMap getMapOriginalVarIndices() { + public void setVarType(VarVersionPair pair, VarType type) { + typeProcessor.setVarType(pair, type); + } + + public int getVarFinal(VarVersionPair pair) { + Integer fin = typeProcessor.getMapFinalVars().get(pair); + return fin == null ? VarTypeProcessor.VAR_FINAL : fin; + } + + public void setVarFinal(VarVersionPair pair, int finalType) { + typeProcessor.getMapFinalVars().put(pair, finalType); + } + + public Map getMapOriginalVarIndices() { return mapOriginalVarIndices; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java b/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java index 273579a..714b4af 100644 --- a/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java +++ b/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.renamer; import org.jetbrains.java.decompiler.struct.StructClass; @@ -21,19 +7,14 @@ import java.util.ArrayList; import java.util.List; public class ClassWrapperNode { - - private StructClass classStruct; - - private ClassWrapperNode superclass; - - private List subclasses = new ArrayList(); + private final StructClass classStruct; + private final List subclasses = new ArrayList<>(); public ClassWrapperNode(StructClass cl) { this.classStruct = cl; } public void addSubclass(ClassWrapperNode node) { - node.setSuperclass(this); subclasses.add(node); } @@ -44,12 +25,4 @@ public class ClassWrapperNode { public List getSubclasses() { return subclasses; } - - public ClassWrapperNode getSuperclass() { - return superclass; - } - - public void setSuperclass(ClassWrapperNode superclass) { - this.superclass = superclass; - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java b/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java index f2cb1ec..4949a00 100644 --- a/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java @@ -1,118 +1,97 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.renamer; +import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer; +import java.util.Arrays; import java.util.HashSet; +import java.util.Locale; +import java.util.Set; public class ConverterHelper implements IIdentifierRenamer { + private static final Set KEYWORDS = new HashSet<>(Arrays.asList( + "abstract", "do", "if", "package", "synchronized", "boolean", "double", "implements", "private", "this", "break", "else", "import", + "protected", "throw", "byte", "extends", "instanceof", "public", "throws", "case", "false", "int", "return", "transient", "catch", + "final", "interface", "short", "true", "char", "finally", "long", "static", "try", "class", "float", "native", "strictfp", "void", + "const", "for", "new", "super", "volatile", "continue", "goto", "null", "switch", "while", "default", "assert", "enum")); + private static final Set RESERVED_WINDOWS_NAMESPACE = new HashSet<>(Arrays.asList( + "con", "prn", "aux", "nul", + "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", + "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9")); - private static HashSet setReserved = new HashSet(); + private int classCounter = 0; + private int fieldCounter = 0; + private int methodCounter = 0; + private final Set setNonStandardClassNames = new HashSet<>(); - static { - setReserved.add("abstract"); - setReserved.add("do"); - setReserved.add("if"); - setReserved.add("package"); - setReserved.add("synchronized"); - setReserved.add("boolean"); - setReserved.add("double"); - setReserved.add("implements"); - setReserved.add("private"); - setReserved.add("this"); - setReserved.add("break"); - setReserved.add("else"); - setReserved.add("import"); - setReserved.add("protected"); - setReserved.add("throw"); - setReserved.add("byte"); - setReserved.add("extends"); - setReserved.add("instanceof"); - setReserved.add("public"); - setReserved.add("throws"); - setReserved.add("case"); - setReserved.add("false"); - setReserved.add("int"); - setReserved.add("return"); - setReserved.add("transient"); - setReserved.add("catch"); - setReserved.add("final"); - setReserved.add("interface"); - setReserved.add("short"); - setReserved.add("true"); - setReserved.add("char"); - setReserved.add("finally"); - setReserved.add("long"); - setReserved.add("static"); - setReserved.add("try"); - setReserved.add("class"); - setReserved.add("float"); - setReserved.add("native"); - setReserved.add("strictfp"); - setReserved.add("void"); - setReserved.add("const"); - setReserved.add("for"); - setReserved.add("new"); - setReserved.add("super"); - setReserved.add("volatile"); - setReserved.add("continue"); - setReserved.add("goto"); - setReserved.add("null"); - setReserved.add("switch"); - setReserved.add("while"); - setReserved.add("default"); - setReserved.add("assert"); - setReserved.add("enum"); + @Override + public boolean toBeRenamed(Type elementType, String className, String element, String descriptor) { + String value = elementType == Type.ELEMENT_CLASS ? className : element; + return value == null || + value.length() <= 2 || + !isValidIdentifier(elementType == Type.ELEMENT_METHOD, value) || + KEYWORDS.contains(value) || + elementType == Type.ELEMENT_CLASS && ( + RESERVED_WINDOWS_NAMESPACE.contains(value.toLowerCase(Locale.ENGLISH)) || + value.length() > 255 - ".class".length()); } - private int class_counter = 0; + /** + * Return {@code true} if, and only if identifier passed is compliant to JLS9 section 3.8 AND DOES NOT CONTAINS so-called "ignorable" characters. + * Ignorable characters are removed by javac silently during compilation and thus may appear only in specially crafted obfuscated classes. + * For more information about "ignorable" characters see JDK-7144981. + * + * @param identifier Identifier to be checked + * @return {@code true} in case {@code identifier} passed can be used as an identifier; {@code false} otherwise. + */ + private static boolean isValidIdentifier(boolean isMethod, String identifier) { - private int field_counter = 0; + assert identifier != null : "Null identifier passed to the isValidIdentifier() method."; + assert !identifier.isEmpty() : "Empty identifier passed to the isValidIdentifier() method."; - private int method_counter = 0; + if (isMethod && (identifier.equals(CodeConstants.INIT_NAME) || identifier.equals(CodeConstants.CLINIT_NAME))) { + return true; + } - private HashSet setNonStandardClassNames = new HashSet(); + if (!Character.isJavaIdentifierStart(identifier.charAt(0))) { + return false; + } + + char[] chars = identifier.toCharArray(); + + for(int i = 1; i < chars.length; i++) { + char ch = chars[i]; + + if ((!Character.isJavaIdentifierPart(ch)) || Character.isIdentifierIgnorable(ch)) { + return false; + } + } + + return true; - public boolean toBeRenamed(int element_type, String classname, String element, String descriptor) { - String value = (element_type == IIdentifierRenamer.ELEMENT_CLASS) ? classname : element; - return value == null || value.length() == 0 || value.length() <= 2 || setReserved.contains(value) || Character.isDigit(value.charAt(0)); } // TODO: consider possible conflicts with not renamed classes, fields and methods! // We should get all relevant information here. - public String getNextClassname(String fullname, String shortname) { - - if (shortname == null) { - return "class_" + (class_counter++); + @Override + public String getNextClassName(String fullName, String shortName) { + if (shortName == null) { + return "class_" + (classCounter++); } int index = 0; - while (Character.isDigit(shortname.charAt(index))) { + while (index < shortName.length() && Character.isDigit(shortName.charAt(index))) { index++; } - if (index == 0 || index == shortname.length()) { - return "class_" + (class_counter++); + if (index == 0 || index == shortName.length()) { + return "class_" + (classCounter++); } else { - String name = shortname.substring(index); - + String name = shortName.substring(index); if (setNonStandardClassNames.contains(name)) { - return "Inner" + name + "_" + (class_counter++); + return "Inner" + name + "_" + (classCounter++); } else { setNonStandardClassNames.add(name); @@ -121,23 +100,25 @@ public class ConverterHelper implements IIdentifierRenamer { } } - public String getNextFieldname(String classname, String field, String descriptor) { - return "field_" + (field_counter++); + @Override + public String getNextFieldName(String className, String field, String descriptor) { + return "field_" + (fieldCounter++); } - public String getNextMethodname(String classname, String method, String descriptor) { - return "method_" + (method_counter++); + @Override + public String getNextMethodName(String className, String method, String descriptor) { + return "method_" + (methodCounter++); } // ***************************************************************************** // static methods // ***************************************************************************** - public static String getSimpleClassName(String fullname) { - return fullname.substring(fullname.lastIndexOf('/') + 1); + public static String getSimpleClassName(String fullName) { + return fullName.substring(fullName.lastIndexOf('/') + 1); } - public static String replaceSimpleClassName(String fullname, String newname) { - return fullname.substring(0, fullname.lastIndexOf('/') + 1) + newname; + public static String replaceSimpleClassName(String fullName, String newName) { + return fullName.substring(0, fullName.lastIndexOf('/') + 1) + newName; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java b/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java index 9ed3578..ba09aa1 100644 --- a/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java +++ b/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java @@ -1,23 +1,7 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.renamer; import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructContext; @@ -25,56 +9,32 @@ import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; -import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.gen.NewClassNameBuilder; import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.io.IOException; import java.util.*; -public class IdentifierConverter { +public class IdentifierConverter implements NewClassNameBuilder { + private final StructContext context; + private final IIdentifierRenamer helper; + private final PoolInterceptor interceptor; + private List rootClasses = new ArrayList<>(); + private List rootInterfaces = new ArrayList<>(); + private Map> interfaceNameMaps = new HashMap<>(); - private StructContext context; - - private IIdentifierRenamer helper; - - private PoolInterceptor interceptor; - - private List rootClasses = new ArrayList(); - - private List rootInterfaces = new ArrayList(); - - private HashMap> interfaceNameMaps = new HashMap>(); - - public void rename(StructContext context) { + public IdentifierConverter(StructContext context, IIdentifierRenamer helper, PoolInterceptor interceptor) { + this.context = context; + this.helper = helper; + this.interceptor = interceptor; + } + public void rename() { try { - this.context = context; - - String user_class = (String)DecompilerContext.getProperty(IFernflowerPreferences.USER_RENAMER_CLASS); - if (user_class != null) { - try { - helper = (IIdentifierRenamer)IdentifierConverter.class.getClassLoader().loadClass(user_class).newInstance(); - } - catch (Exception ex) { - // ignore errors - } - } - - if (helper == null) { - helper = new ConverterHelper(); - } - - interceptor = new PoolInterceptor(helper); - buildInheritanceTree(); - renameAllClasses(); - renameInterfaces(); - renameClasses(); - - DecompilerContext.setPoolInterceptor(interceptor); context.reloadContext(); } catch (IOException ex) { @@ -83,32 +43,29 @@ public class IdentifierConverter { } private void renameClasses() { - List lstClasses = getReversePostOrderListIterative(rootClasses); - - HashMap> classNameMaps = new HashMap>(); + Map> classNameMaps = new HashMap<>(); for (ClassWrapperNode node : lstClasses) { - StructClass cl = node.getClassStruct(); - HashMap names = new HashMap(); + Map names = new HashMap<>(); - // merge informations on super class + // merge information on super class if (cl.superClass != null) { - HashMap mapClass = classNameMaps.get(cl.superClass.getString()); + Map mapClass = classNameMaps.get(cl.superClass.getString()); if (mapClass != null) { names.putAll(mapClass); } } - // merge informations on interfaces - for (String intrName : cl.getInterfaceNames()) { - HashMap mapInt = interfaceNameMaps.get(intrName); + // merge information on interfaces + for (String ifName : cl.getInterfaceNames()) { + Map mapInt = interfaceNameMaps.get(ifName); if (mapInt != null) { names.putAll(mapInt); } else { - StructClass clintr = context.getClass(intrName); + StructClass clintr = context.getClass(ifName); if (clintr != null) { names.putAll(processExternalInterface(clintr)); } @@ -123,18 +80,16 @@ public class IdentifierConverter { } } - private HashMap processExternalInterface(StructClass cl) { + private Map processExternalInterface(StructClass cl) { + Map names = new HashMap<>(); - HashMap names = new HashMap(); - - for (String intrName : cl.getInterfaceNames()) { - - HashMap mapInt = interfaceNameMaps.get(intrName); + for (String ifName : cl.getInterfaceNames()) { + Map mapInt = interfaceNameMaps.get(ifName); if (mapInt != null) { names.putAll(mapInt); } else { - StructClass clintr = context.getClass(intrName); + StructClass clintr = context.getClass(ifName); if (clintr != null) { names.putAll(processExternalInterface(clintr)); } @@ -147,20 +102,18 @@ public class IdentifierConverter { } private void renameInterfaces() { - List lstInterfaces = getReversePostOrderListIterative(rootInterfaces); - - HashMap> interfaceNameMaps = new HashMap>(); + Map> interfaceNameMaps = new HashMap<>(); // rename methods and fields for (ClassWrapperNode node : lstInterfaces) { StructClass cl = node.getClassStruct(); - HashMap names = new HashMap(); + Map names = new HashMap<>(); - // merge informations on super interfaces - for (String intrName : cl.getInterfaceNames()) { - HashMap mapInt = interfaceNameMaps.get(intrName); + // merge information on super interfaces + for (String ifName : cl.getInterfaceNames()) { + Map mapInt = interfaceNameMaps.get(ifName); if (mapInt != null) { names.putAll(mapInt); } @@ -175,9 +128,8 @@ public class IdentifierConverter { } private void renameAllClasses() { - // order not important - List lstAllClasses = new ArrayList(getReversePostOrderListIterative(rootInterfaces)); + List lstAllClasses = new ArrayList<>(getReversePostOrderListIterative(rootInterfaces)); lstAllClasses.addAll(getReversePostOrderListIterative(rootClasses)); // rename all interfaces and classes @@ -195,14 +147,13 @@ public class IdentifierConverter { String classOldFullName = cl.qualifiedName; // TODO: rename packages - String clsimplename = ConverterHelper.getSimpleClassName(classOldFullName); - if (helper.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, clsimplename, null, null)) { + String clSimpleName = ConverterHelper.getSimpleClassName(classOldFullName); + if (helper.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_CLASS, clSimpleName, null, null)) { String classNewFullName; do { - classNewFullName = ConverterHelper.replaceSimpleClassName(classOldFullName, - helper.getNextClassname(classOldFullName, ConverterHelper - .getSimpleClassName(classOldFullName))); + String classname = helper.getNextClassName(classOldFullName, ConverterHelper.getSimpleClassName(classOldFullName)); + classNewFullName = ConverterHelper.replaceSimpleClassName(classOldFullName, classname); } while (context.getClasses().containsKey(classNewFullName)); @@ -210,8 +161,7 @@ public class IdentifierConverter { } } - private void renameClassIdentifiers(StructClass cl, HashMap names) { - + private void renameClassIdentifiers(StructClass cl, Map names) { // all classes are already renamed String classOldFullName = cl.qualifiedName; String classNewFullName = interceptor.getName(classOldFullName); @@ -221,7 +171,7 @@ public class IdentifierConverter { } // methods - HashSet setMethodNames = new HashSet(); + HashSet setMethodNames = new HashSet<>(); for (StructMethod md : cl.getMethods()) { setMethodNames.add(md.getName()); } @@ -241,10 +191,10 @@ public class IdentifierConverter { names.put(key, name); } } - else if (helper.toBeRenamed(IIdentifierRenamer.ELEMENT_METHOD, classOldFullName, name, mt.getDescriptor())) { + else if (helper.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_METHOD, classOldFullName, name, mt.getDescriptor())) { if (isPrivate || !names.containsKey(key)) { do { - name = helper.getNextMethodname(classOldFullName, name, mt.getDescriptor()); + name = helper.getNextMethodName(classOldFullName, name, mt.getDescriptor()); } while (setMethodNames.contains(name)); @@ -268,85 +218,48 @@ public class IdentifierConverter { // fields // FIXME: should overloaded fields become the same name? - HashSet setFieldNames = new HashSet(); + HashSet setFieldNames = new HashSet<>(); for (StructField fd : cl.getFields()) { setFieldNames.add(fd.getName()); } for (StructField fd : cl.getFields()) { - if (helper.toBeRenamed(IIdentifierRenamer.ELEMENT_FIELD, classOldFullName, fd.getName(), fd.getDescriptor())) { - String newname; - + if (helper.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_FIELD, classOldFullName, fd.getName(), fd.getDescriptor())) { + String newName; do { - newname = helper.getNextFieldname(classOldFullName, fd.getName(), fd.getDescriptor()); + newName = helper.getNextFieldName(classOldFullName, fd.getName(), fd.getDescriptor()); } - while (setFieldNames.contains(newname)); + while (setFieldNames.contains(newName)); interceptor.addName(classOldFullName + " " + fd.getName() + " " + fd.getDescriptor(), - classNewFullName + " " + newname + " " + buildNewDescriptor(true, fd.getDescriptor())); + classNewFullName + " " + newName + " " + buildNewDescriptor(true, fd.getDescriptor())); } } } + @Override + public String buildNewClassname(String className) { + return interceptor.getName(className); + } + private String buildNewDescriptor(boolean isField, String descriptor) { - - boolean updated = false; - + String newDescriptor; if (isField) { - FieldDescriptor fd = FieldDescriptor.parseDescriptor(descriptor); - - VarType ftype = fd.type; - if (ftype.type == CodeConstants.TYPE_OBJECT) { - String newclname = interceptor.getName(ftype.value); - if (newclname != null) { - ftype.value = newclname; - updated = true; - } - } - - if (updated) { - return fd.getDescriptor(); - } + newDescriptor = FieldDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this); } else { - - MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor); - // params - for (VarType partype : md.params) { - if (partype.type == CodeConstants.TYPE_OBJECT) { - String newclname = interceptor.getName(partype.value); - if (newclname != null) { - partype.value = newclname; - updated = true; - } - } - } - - // return value - if (md.ret.type == CodeConstants.TYPE_OBJECT) { - String newclname = interceptor.getName(md.ret.value); - if (newclname != null) { - md.ret.value = newclname; - updated = true; - } - } - - if (updated) { - return md.getDescriptor(); - } + newDescriptor = MethodDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this); } - - return descriptor; + return newDescriptor != null ? newDescriptor : descriptor; } private static List getReversePostOrderListIterative(List roots) { + List res = new ArrayList<>(); - List res = new ArrayList(); + LinkedList stackNode = new LinkedList<>(); + LinkedList stackIndex = new LinkedList<>(); - LinkedList stackNode = new LinkedList(); - LinkedList stackIndex = new LinkedList(); - - HashSet setVisited = new HashSet(); + Set setVisited = new HashSet<>(); for (ClassWrapperNode root : roots) { stackNode.add(root); @@ -354,7 +267,6 @@ public class IdentifierConverter { } while (!stackNode.isEmpty()) { - ClassWrapperNode node = stackNode.getLast(); int index = stackIndex.removeLast(); @@ -366,17 +278,14 @@ public class IdentifierConverter { ClassWrapperNode sub = lstSubs.get(index); if (!setVisited.contains(sub)) { stackIndex.add(index + 1); - stackNode.add(sub); stackIndex.add(0); - break; } } if (index == lstSubs.size()) { res.add(0, node); - stackNode.removeLast(); } } @@ -384,37 +293,33 @@ public class IdentifierConverter { return res; } - private void buildInheritanceTree() { - - Map nodes = new HashMap(); + Map nodes = new HashMap<>(); Map classes = context.getClasses(); - List rootClasses = new ArrayList(); - List rootInterfaces = new ArrayList(); + List rootClasses = new ArrayList<>(); + List rootInterfaces = new ArrayList<>(); for (StructClass cl : classes.values()) { - if (!cl.isOwn()) { continue; } - LinkedList stack = new LinkedList(); - LinkedList stackSubnodes = new LinkedList(); + LinkedList stack = new LinkedList<>(); + LinkedList stackSubNodes = new LinkedList<>(); stack.add(cl); - stackSubnodes.add(null); + stackSubNodes.add(null); while (!stack.isEmpty()) { + StructClass clStr = stack.removeFirst(); + ClassWrapperNode child = stackSubNodes.removeFirst(); - StructClass clstr = stack.removeFirst(); - ClassWrapperNode child = stackSubnodes.removeFirst(); - - ClassWrapperNode node = nodes.get(clstr.qualifiedName); + ClassWrapperNode node = nodes.get(clStr.qualifiedName); boolean isNewNode = (node == null); if (isNewNode) { - nodes.put(clstr.qualifiedName, node = new ClassWrapperNode(clstr)); + nodes.put(clStr.qualifiedName, node = new ClassWrapperNode(clStr)); } if (child != null) { @@ -425,28 +330,25 @@ public class IdentifierConverter { break; } else { - boolean isInterface = clstr.hasModifier(CodeConstants.ACC_INTERFACE); + boolean isInterface = clStr.hasModifier(CodeConstants.ACC_INTERFACE); boolean found_parent = false; if (isInterface) { - for (String intrName : clstr.getInterfaceNames()) { - StructClass clparent = classes.get(intrName); - if (clparent != null) { - stack.add(clparent); - stackSubnodes.add(node); + for (String ifName : clStr.getInterfaceNames()) { + StructClass clParent = classes.get(ifName); + if (clParent != null) { + stack.add(clParent); + stackSubNodes.add(node); found_parent = true; } } } - else { - if (clstr.superClass != null) { // null iff java/lang/Object - StructClass clparent = classes.get(clstr.superClass.getString()); - - if (clparent != null) { - stack.add(clparent); - stackSubnodes.add(node); - found_parent = true; - } + else if (clStr.superClass != null) { // null iff java/lang/Object + StructClass clParent = classes.get(clStr.superClass.getString()); + if (clParent != null) { + stack.add(clParent); + stackSubNodes.add(node); + found_parent = true; } } @@ -460,4 +362,4 @@ public class IdentifierConverter { this.rootClasses = rootClasses; this.rootInterfaces = rootInterfaces; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java b/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java index 74e2ed8..63cb222 100644 --- a/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java +++ b/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java @@ -1,35 +1,12 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.modules.renamer; -import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer; - import java.util.HashMap; +import java.util.Map; public class PoolInterceptor { - - private IIdentifierRenamer helper; - - private HashMap mapOldToNewNames = new HashMap(); - - private HashMap mapNewToOldNames = new HashMap(); - - public PoolInterceptor(IIdentifierRenamer helper) { - this.helper = helper; - } + private final Map mapOldToNewNames = new HashMap<>(); + private final Map mapNewToOldNames = new HashMap<>(); public void addName(String oldName, String newName) { mapOldToNewNames.put(oldName, newName); @@ -43,8 +20,4 @@ public class PoolInterceptor { public String getOldName(String newName) { return mapNewToOldNames.get(newName); } - - public IIdentifierRenamer getHelper() { - return helper; - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java index 819ae64..c77cfaa 100644 --- a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java +++ b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java @@ -1,20 +1,8 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.extern.IResultSaver; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader.Link; @@ -40,11 +28,11 @@ public class ContextUnit { private final IResultSaver resultSaver; private final IDecompiledData decompiledData; - private final List classEntries = new ArrayList(); // class file or jar/zip entry - private final List dirEntries = new ArrayList(); - private final List otherEntries = new ArrayList(); + private final List classEntries = new ArrayList<>(); // class file or jar/zip entry + private final List dirEntries = new ArrayList<>(); + private final List otherEntries = new ArrayList<>(); - private List classes = new ArrayList(); + private List classes = new ArrayList<>(); private Manifest manifest; public ContextUnit(int type, String archivePath, String filename, boolean own, IResultSaver resultSaver, IDecompiledData decompiledData) { @@ -70,18 +58,14 @@ public class ContextUnit { } public void reload(LazyLoader loader) throws IOException { - List lstClasses = new ArrayList(); + List lstClasses = new ArrayList<>(); for (StructClass cl : classes) { String oldName = cl.qualifiedName; StructClass newCl; - DataInputFullStream in = loader.getClassStream(oldName); - try { - newCl = new StructClass(in, cl.isOwn(), loader); - } - finally { - in.close(); + try (DataInputFullStream in = loader.getClassStream(oldName)) { + newCl = StructClass.create(in, cl.isOwn(), loader); } lstClasses.add(newCl); @@ -108,11 +92,18 @@ 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); if (content != null) { - resultSaver.saveClassFile(filename, cl.qualifiedName, entryName, content); + int[] mapping = null; + if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) { + mapping = DecompilerContext.getBytecodeSourceMapper().getOriginalLinesMapping(); + } + resultSaver.saveClassFile(filename, cl.qualifiedName, entryName, content, mapping); } } } @@ -162,4 +153,4 @@ public class ContextUnit { public List getClasses() { return classes; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/IDecompiledData.java b/src/org/jetbrains/java/decompiler/struct/IDecompiledData.java index f309b0e..b3b38dd 100644 --- a/src/org/jetbrains/java/decompiler/struct/IDecompiledData.java +++ b/src/org/jetbrains/java/decompiler/struct/IDecompiledData.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct; public interface IDecompiledData { diff --git a/src/org/jetbrains/java/decompiler/struct/StructClass.java b/src/org/jetbrains/java/decompiler/struct/StructClass.java index 13f5875..134813e 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructClass.java +++ b/src/org/jetbrains/java/decompiler/struct/StructClass.java @@ -1,21 +1,9 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -24,7 +12,7 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.io.IOException; -import java.util.HashMap; +import java.util.List; import java.util.Map; /* @@ -48,10 +36,52 @@ import java.util.Map; } */ public class StructClass extends StructMember { + 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); + + ConstantPool pool = new ConstantPool(in); + + int accessFlags = in.readUnsignedShort(); + int thisClassIdx = in.readUnsignedShort(); + int superClassIdx = in.readUnsignedShort(); + String qualifiedName = pool.getPrimitiveConstant(thisClassIdx).getString(); + PrimitiveConstant superClass = pool.getPrimitiveConstant(superClassIdx); + + int length = in.readUnsignedShort(); + 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(); + } + + length = in.readUnsignedShort(); + VBStyleCollectionfields = new VBStyleCollection<>(length); + for (int i = 0; i < length; i++) { + StructField field = StructField.create(in, pool, qualifiedName); + fields.addWithKey(field, InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor())); + } + + length = in.readUnsignedShort(); + VBStyleCollectionmethods = new VBStyleCollection<>(length); + for (int i = 0; i < length; i++) { + StructMethod method = StructMethod.create(in, pool, qualifiedName, bytecodeVersion, own); + methods.addWithKey(method, InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor())); + } + + Map attributes = readAttributes(in, pool); + + 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; @@ -60,60 +90,32 @@ public class StructClass extends StructMember { private final String[] interfaceNames; private final VBStyleCollection fields; private final VBStyleCollection methods; - public final Map> enumSwitchMap = new HashMap>(); 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 { + 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; - - in.discard(4); - - minorVersion = in.readUnsignedShort(); - majorVersion = in.readUnsignedShort(); - - pool = new ConstantPool(in); - - accessFlags = in.readUnsignedShort(); - int thisClassIdx = in.readUnsignedShort(); - int superClassIdx = in.readUnsignedShort(); - qualifiedName = pool.getPrimitiveConstant(thisClassIdx).getString(); - superClass = pool.getPrimitiveConstant(superClassIdx); - - // interfaces - int length = in.readUnsignedShort(); - interfaces = new int[length]; - 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(); - for (int i = 0; i < length; i++) { - StructField field = new StructField(in, this); - fields.addWithKey(field, InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor())); - } - - // methods - length = in.readUnsignedShort(); - methods = new VBStyleCollection(); - for (int i = 0; i < length; i++) { - StructMethod method = new StructMethod(in, this); - methods.addWithKey(method, InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor())); - } - - // attributes - attributes = readAttributes(in, pool); - - releaseResources(); + 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) { @@ -149,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; } @@ -173,26 +184,17 @@ public class StructClass extends StructMember { return loader; } - public boolean isVersionGE_1_5() { - return (majorVersion > 48 || (majorVersion == 48 && minorVersion > 0)); // FIXME: check second condition + 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 >= 51); + public boolean isVersion8() { + return majorVersion >= CodeConstants.BYTECODE_JAVA_8; } - public int getBytecodeVersion() { - switch (majorVersion) { - case 52: - return CodeConstants.BYTECODE_JAVA_8; - case 51: - return CodeConstants.BYTECODE_JAVA_7; - case 50: - return CodeConstants.BYTECODE_JAVA_6; - case 49: - return CodeConstants.BYTECODE_JAVA_5; - } - - return CodeConstants.BYTECODE_JAVA_LE_4; + @Override + public String toString() { + return qualifiedName; } } diff --git a/src/org/jetbrains/java/decompiler/struct/StructContext.java b/src/org/jetbrains/java/decompiler/struct/StructContext.java index 7bfb139..61486aa 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructContext.java +++ b/src/org/jetbrains/java/decompiler/struct/StructContext.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -31,12 +17,11 @@ 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; - private final Map units = new HashMap(); - private final Map classes = new HashMap(); + private final Map units = new HashMap<>(); + private final Map classes = new HashMap<>(); public StructContext(IResultSaver saver, IDecompiledData decompiledData, LazyLoader loader) { this.saver = saver; @@ -119,17 +104,11 @@ public class StructContext { } if (filename.endsWith(".class")) { - try { - DataInputFullStream in = loader.getClassStream(file.getAbsolutePath(), null); - try { - StructClass cl = new StructClass(in, isOwn, loader); - classes.put(cl.qualifiedName, cl); - unit.addClass(cl, filename); - loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(LazyLoader.Link.CLASS, file.getAbsolutePath(), null)); - } - finally { - in.close(); - } + try (DataInputFullStream in = loader.getClassStream(file.getAbsolutePath(), null)) { + 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)); } catch (IOException ex) { String message = "Corrupted class file: " + file; @@ -143,10 +122,7 @@ public class StructContext { } private void addArchive(String path, File file, int type, boolean isOwn) throws IOException { - @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") - ZipFile archive = type == ContextUnit.TYPE_JAR ? new JarFile(file) : new ZipFile(file); - - try { + try (ZipFile archive = type == ContextUnit.TYPE_JAR ? new JarFile(file) : new ZipFile(file)) { Enumeration entries = archive.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); @@ -164,10 +140,10 @@ 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(LazyLoader.Link.ENTRY, file.getAbsolutePath(), name)); + loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), name)); } else { unit.addOtherEntry(file.getAbsolutePath(), name); @@ -178,9 +154,6 @@ public class StructContext { } } } - finally { - archive.close(); - } } public Map getClasses() { diff --git a/src/org/jetbrains/java/decompiler/struct/StructField.java b/src/org/jetbrains/java/decompiler/struct/StructField.java index 796d77f..cfa64d2 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructField.java +++ b/src/org/jetbrains/java/decompiler/struct/StructField.java @@ -1,24 +1,12 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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 { @@ -30,29 +18,37 @@ 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(); + + String[] values = pool.getClassElement(ConstantPool.FIELD, clQualifiedName, nameIndex, descriptorIndex); + + Map attributes = readAttributes(in, pool); + + return new StructField(accessFlags, attributes, values[0], values[1]); + } private final String name; private final String descriptor; - - public StructField(DataInputFullStream in, StructClass clStruct) throws IOException { - accessFlags = in.readUnsignedShort(); - int nameIndex = in.readUnsignedShort(); - int descriptorIndex = in.readUnsignedShort(); - - ConstantPool pool = clStruct.getPool(); - String[] values = pool.getClassElement(ConstantPool.FIELD, clStruct.qualifiedName, nameIndex, descriptorIndex); - name = values[0]; - descriptor = values[1]; - - 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; } + + @Override + public String toString() { + return name; + } } diff --git a/src/org/jetbrains/java/decompiler/struct/StructMember.java b/src/org/jetbrains/java/decompiler/struct/StructMember.java index f681e19..920e7f9 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMember.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMember.java @@ -1,41 +1,37 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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.StructLocalVariableTableAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTypeTableAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.util.DataInputFullStream; -import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; -public class StructMember { - - protected int accessFlags; - protected VBStyleCollection 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 VBStyleCollection getAttributes() { - return attributes; + public T getAttribute(StructGeneralAttribute.Key attribute) { + @SuppressWarnings("unchecked") T t = (T)attributes.get(attribute.name); + return t; + } + + public boolean hasAttribute(StructGeneralAttribute.Key attribute) { + return attributes.containsKey(attribute.name); } public boolean hasModifier(int modifier) { @@ -43,45 +39,40 @@ public class StructMember { } public boolean isSynthetic() { - return hasModifier(CodeConstants.ACC_SYNTHETIC) || attributes.containsKey(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC); + return hasModifier(CodeConstants.ACC_SYNTHETIC) || hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC); } - protected VBStyleCollection readAttributes(DataInputFullStream in, ConstantPool pool) throws IOException { - VBStyleCollection attributes = new VBStyleCollection(); - + 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.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.getWithKey(name); - table.addLocalVariableTable((StructLocalVariableTableAttribute)attribute); + StructLocalVariableTableAttribute table = (StructLocalVariableTableAttribute)attributes.get(name); + table.add((StructLocalVariableTableAttribute)attribute); + } + 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.addWithKey(attribute, attribute.getName()); + attributes.put(name, attribute); } } } return attributes; } - - protected StructGeneralAttribute readAttribute(DataInputFullStream in, ConstantPool pool, String name) throws IOException { - StructGeneralAttribute attribute = StructGeneralAttribute.createAttribute(name); - if (attribute == null) { - in.discard(in.readInt()); - } - else { - byte[] data = new byte[in.readInt()]; - in.readFull(data); - attribute.setInfo(data); - attribute.initContent(pool); - } - return attribute; - } } diff --git a/src/org/jetbrains/java/decompiler/struct/StructMethod.java b/src/org/jetbrains/java/decompiler/struct/StructMethod.java index 2b5249f..a715593 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMethod.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMethod.java @@ -1,22 +1,10 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; import org.jetbrains.java.decompiler.util.DataInputFullStream; import org.jetbrains.java.decompiler.util.VBStyleCollection; @@ -24,6 +12,7 @@ import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import static org.jetbrains.java.decompiler.code.CodeConstants.*; @@ -37,81 +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 VBStyleCollection 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.addAllWithKey(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; + } + else { + this.localVariables = this.codeLength = this.codeFullLength = -1; } } - @Override - protected StructGeneralAttribute readAttribute(DataInputFullStream in, ConstantPool pool, String name) throws IOException { - if (StructGeneralAttribute.ATTRIBUTE_CODE.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; - } - - 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() throws IOException { - if (containsCode && expanded) { + public void releaseResources() { + if (codeLength >= 0 && expanded) { seq = null; expanded = false; } @@ -119,12 +93,9 @@ public class StructMethod extends StructMember { @SuppressWarnings("AssignmentToForLoopParameter") private InstructionSequence parseBytecode(DataInputFullStream in, int length, ConstantPool pool) throws IOException { - VBStyleCollection instructions = new VBStyleCollection(); - - int bytecode_version = classStruct.getBytecodeVersion(); + VBStyleCollection instructions = new VBStyleCollection<>(); for (int i = 0; i < length; ) { - int offset = i; int opcode = in.readUnsignedByte(); @@ -137,29 +108,29 @@ public class StructMethod extends StructMember { opcode = in.readUnsignedByte(); } - List operands = new ArrayList(); + List operands = new ArrayList<>(); if (opcode >= opc_iconst_m1 && opcode <= opc_iconst_5) { - operands.add(new Integer(opr_iconst[opcode - opc_iconst_m1])); + operands.add(opr_iconst[opcode - opc_iconst_m1]); opcode = opc_bipush; } else if (opcode >= opc_iload_0 && opcode <= opc_aload_3) { - operands.add(new Integer(opr_loadstore[opcode - opc_iload_0])); + operands.add(opr_loadstore[opcode - opc_iload_0]); opcode = opcs_load[(opcode - opc_iload_0) / 4]; } else if (opcode >= opc_istore_0 && opcode <= opc_astore_3) { - operands.add(new Integer(opr_loadstore[opcode - opc_istore_0])); + operands.add(opr_loadstore[opcode - opc_istore_0]); opcode = opcs_store[(opcode - opc_istore_0) / 4]; } else { switch (opcode) { case opc_bipush: - operands.add(new Integer(in.readByte())); + operands.add((int)in.readByte()); i++; break; case opc_ldc: case opc_newarray: - operands.add(new Integer(in.readUnsignedByte())); + operands.add(in.readUnsignedByte()); i++; break; case opc_sipush: @@ -184,7 +155,7 @@ public class StructMethod extends StructMember { if (opcode != opc_sipush) { group = GROUP_JUMP; } - operands.add(new Integer(in.readShort())); + operands.add((int)in.readShort()); i += 2; break; case opc_ldc_w: @@ -200,7 +171,7 @@ public class StructMethod extends StructMember { case opc_anewarray: case opc_checkcast: case opc_instanceof: - operands.add(new Integer(in.readUnsignedShort())); + operands.add(in.readUnsignedShort()); i += 2; if (opcode >= opc_getstatic && opcode <= opc_putfield) { group = GROUP_FIELDACCESS; @@ -210,8 +181,8 @@ public class StructMethod extends StructMember { } break; case opc_invokedynamic: - if (classStruct.isVersionGE_1_7()) { // instruction unused in Java 6 and before - operands.add(new Integer(in.readUnsignedShort())); + if (bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) { // instruction unused in Java 6 and before + operands.add(in.readUnsignedShort()); in.discard(2); group = GROUP_INVOCATION; i += 4; @@ -229,11 +200,11 @@ public class StructMethod extends StructMember { case opc_astore: case opc_ret: if (wide) { - operands.add(new Integer(in.readUnsignedShort())); + operands.add(in.readUnsignedShort()); i += 2; } else { - operands.add(new Integer(in.readUnsignedByte())); + operands.add(in.readUnsignedByte()); i++; } if (opcode == opc_ret) { @@ -242,49 +213,49 @@ public class StructMethod extends StructMember { break; case opc_iinc: if (wide) { - operands.add(new Integer(in.readUnsignedShort())); - operands.add(new Integer(in.readShort())); + operands.add(in.readUnsignedShort()); + operands.add((int)in.readShort()); i += 4; } else { - operands.add(new Integer(in.readUnsignedByte())); - operands.add(new Integer(in.readByte())); + operands.add(in.readUnsignedByte()); + operands.add((int)in.readByte()); i += 2; } break; case opc_goto_w: case opc_jsr_w: opcode = opcode == opc_jsr_w ? opc_jsr : opc_goto; - operands.add(new Integer(in.readInt())); + operands.add(in.readInt()); group = GROUP_JUMP; i += 4; break; case opc_invokeinterface: - operands.add(new Integer(in.readUnsignedShort())); - operands.add(new Integer(in.readUnsignedByte())); + operands.add(in.readUnsignedShort()); + operands.add(in.readUnsignedByte()); in.discard(1); group = GROUP_INVOCATION; i += 4; break; case opc_multianewarray: - operands.add(new Integer(in.readUnsignedShort())); - operands.add(new Integer(in.readUnsignedByte())); + operands.add(in.readUnsignedShort()); + operands.add(in.readUnsignedByte()); i += 3; break; case opc_tableswitch: in.discard((4 - (i + 1) % 4) % 4); i += ((4 - (i + 1) % 4) % 4); // padding - operands.add(new Integer(in.readInt())); + operands.add(in.readInt()); i += 4; int low = in.readInt(); - operands.add(new Integer(low)); + operands.add(low); i += 4; int high = in.readInt(); - operands.add(new Integer(high)); + operands.add(high); i += 4; for (int j = 0; j < high - low + 1; j++) { - operands.add(new Integer(in.readInt())); + operands.add(in.readInt()); i += 4; } group = GROUP_SWITCH; @@ -293,16 +264,16 @@ public class StructMethod extends StructMember { case opc_lookupswitch: in.discard((4 - (i + 1) % 4) % 4); i += ((4 - (i + 1) % 4) % 4); // padding - operands.add(new Integer(in.readInt())); + operands.add(in.readInt()); i += 4; int npairs = in.readInt(); - operands.add(new Integer(npairs)); + operands.add(npairs); i += 4; for (int j = 0; j < npairs; j++) { - operands.add(new Integer(in.readInt())); + operands.add(in.readInt()); i += 4; - operands.add(new Integer(in.readInt())); + operands.add(in.readInt()); i += 4; } group = GROUP_SWITCH; @@ -318,20 +289,23 @@ public class StructMethod extends StructMember { } } - int[] ops = new int[operands.size()]; - for (int j = 0; j < operands.size(); j++) { - ops[j] = operands.get(j).intValue(); + int[] ops = null; + if (!operands.isEmpty()) { + ops = new int[operands.size()]; + for (int j = 0; j < operands.size(); j++) { + ops[j] = operands.get(j); + } } - Instruction instr = ConstantsUtil.getInstructionInstance(opcode, wide, group, bytecode_version, ops); + Instruction instr = Instruction.create(opcode, wide, group, bytecodeVersion, ops); - instructions.addWithKey(instr, new Integer(offset)); + instructions.addWithKey(instr, offset); i++; } // initialize exception table - List lstHandlers = new ArrayList(); + List lstHandlers = new ArrayList<>(); int exception_count = in.readUnsignedShort(); for (int i = 0; i < exception_count; i++) { @@ -341,7 +315,6 @@ public class StructMethod extends StructMember { handler.handler = in.readUnsignedShort(); int excclass = in.readUnsignedShort(); - handler.class_index = excclass; if (excclass != 0) { handler.exceptionClass = pool.getPrimitiveConstant(excclass).getString(); } @@ -366,10 +339,6 @@ public class StructMethod extends StructMember { return seq; } - public StructClass getClassStruct() { - return classStruct; - } - public String getName() { return name; } @@ -378,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() { @@ -389,4 +362,13 @@ public class StructMethod extends StructMember { public InstructionSequence getInstructionSequence() { return seq; } + + public StructLocalVariableTableAttribute getLocalVariableAttr() { + return getAttribute(StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE); + } + + @Override + public String toString() { + return name; + } } 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/StructAnnDefaultAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnDefaultAttribute.java index fbad47e..891ffba 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnDefaultAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnDefaultAttribute.java @@ -1,22 +1,9 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.IOException; @@ -25,8 +12,8 @@ public class StructAnnDefaultAttribute extends StructGeneralAttribute { private Exprent defaultValue; @Override - public void initContent(ConstantPool pool) throws IOException { - defaultValue = StructAnnotationAttribute.parseAnnotationElement(stream(), pool); + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { + defaultValue = StructAnnotationAttribute.parseAnnotationElement(data, pool); } public Exprent getDefaultValue() { diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java index d04cb04..dec6b28 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -21,6 +7,7 @@ import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.DataInputStream; import java.io.IOException; @@ -29,18 +16,17 @@ import java.util.Collections; import java.util.List; public class StructAnnotationAttribute extends StructGeneralAttribute { - private List annotations; @Override - public void initContent(ConstantPool pool) throws IOException { - annotations = parseAnnotations(pool, stream()); + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { + annotations = parseAnnotations(pool, data); } public static List parseAnnotations(ConstantPool pool, DataInputStream data) throws IOException { int len = data.readUnsignedShort(); if (len > 0) { - List annotations = new ArrayList(len); + List annotations = new ArrayList<>(len); for (int i = 0; i < len; i++) { annotations.add(parseAnnotation(data, pool)); } @@ -58,8 +44,8 @@ public class StructAnnotationAttribute extends StructGeneralAttribute { List values; int len = data.readUnsignedShort(); if (len > 0) { - names = new ArrayList(len); - values = new ArrayList(len); + names = new ArrayList<>(len); + values = new ArrayList<>(len); for (int i = 0; i < len; i++) { names.add(pool.getPrimitiveConstant(data.readUnsignedShort()).getString()); values.add(parseAnnotationElement(data, pool)); @@ -81,7 +67,7 @@ public class StructAnnotationAttribute extends StructGeneralAttribute { String className = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); String constName = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); FieldDescriptor descr = FieldDescriptor.parseDescriptor(className); - return new FieldExprent(constName, descr.type.value, true, null, descr); + return new FieldExprent(constName, descr.type.value, true, null, descr, null); case 'c': // class String descriptor = pool.getPrimitiveConstant(data.readUnsignedShort()).getString(); @@ -122,13 +108,13 @@ public class StructAnnotationAttribute extends StructGeneralAttribute { default: throw new RuntimeException("invalid class type: " + type.type); } - return new ConstExprent(VarType.VARTYPE_CLASS, value); + return new ConstExprent(VarType.VARTYPE_CLASS, value, null); case '[': // array List elements = Collections.emptyList(); int len = data.readUnsignedShort(); if (len > 0) { - elements = new ArrayList(len); + elements = new ArrayList<>(len); for (int i = 0; i < len; i++) { elements.add(parseAnnotationElement(data, pool)); } @@ -143,7 +129,7 @@ public class StructAnnotationAttribute extends StructGeneralAttribute { newType = new VarType(elementType.type, 1, elementType.value); } - NewExprent newExpr = new NewExprent(newType, Collections.emptyList()); + NewExprent newExpr = new NewExprent(newType, Collections.emptyList(), null); newExpr.setDirectArrayInit(true); newExpr.setLstArrayElements(elements); return newExpr; @@ -155,23 +141,23 @@ public class StructAnnotationAttribute extends StructGeneralAttribute { PrimitiveConstant cn = pool.getPrimitiveConstant(data.readUnsignedShort()); switch (tag) { case 'B': - return new ConstExprent(VarType.VARTYPE_BYTE, cn.value); + return new ConstExprent(VarType.VARTYPE_BYTE, cn.value, null); case 'C': - return new ConstExprent(VarType.VARTYPE_CHAR, cn.value); + return new ConstExprent(VarType.VARTYPE_CHAR, cn.value, null); case 'D': - return new ConstExprent(VarType.VARTYPE_DOUBLE, cn.value); + return new ConstExprent(VarType.VARTYPE_DOUBLE, cn.value, null); case 'F': - return new ConstExprent(VarType.VARTYPE_FLOAT, cn.value); + return new ConstExprent(VarType.VARTYPE_FLOAT, cn.value, null); case 'I': - return new ConstExprent(VarType.VARTYPE_INT, cn.value); + return new ConstExprent(VarType.VARTYPE_INT, cn.value, null); case 'J': - return new ConstExprent(VarType.VARTYPE_LONG, cn.value); + return new ConstExprent(VarType.VARTYPE_LONG, cn.value, null); case 'S': - return new ConstExprent(VarType.VARTYPE_SHORT, cn.value); + return new ConstExprent(VarType.VARTYPE_SHORT, cn.value, null); case 'Z': - return new ConstExprent(VarType.VARTYPE_BOOLEAN, cn.value); + return new ConstExprent(VarType.VARTYPE_BOOLEAN, cn.value, null); case 's': - return new ConstExprent(VarType.VARTYPE_STRING, cn.value); + return new ConstExprent(VarType.VARTYPE_STRING, cn.value, null); default: throw new RuntimeException("invalid element type!"); } @@ -181,4 +167,4 @@ public class StructAnnotationAttribute extends StructGeneralAttribute { public List getAnnotations() { return annotations; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java index a33c7bb..d3f0de5 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java @@ -1,24 +1,10 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; -import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -29,12 +15,10 @@ public class StructAnnotationParameterAttribute extends StructGeneralAttribute { private List> paramAnnotations; @Override - public void initContent(ConstantPool pool) throws IOException { - DataInputStream data = stream(); - + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { int len = data.readUnsignedByte(); if (len > 0) { - paramAnnotations = new ArrayList>(len); + paramAnnotations = new ArrayList<>(len); for (int i = 0; i < len; i++) { List annotations = StructAnnotationAttribute.parseAnnotations(pool, data); paramAnnotations.add(annotations); diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java deleted file mode 100644 index 59324e3..0000000 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.struct.attr; - -import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; -import org.jetbrains.java.decompiler.struct.consts.ConstantPool; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class StructAnnotationTypeAttribute extends StructGeneralAttribute { - - private static final int ANNOTATION_TARGET_TYPE_GENERIC_CLASS = 0x00; - private static final int ANNOTATION_TARGET_TYPE_GENERIC_METHOD = 0x01; - private static final int ANNOTATION_TARGET_TYPE_EXTENDS_IMPLEMENTS = 0x10; - private static final int ANNOTATION_TARGET_TYPE_GENERIC_CLASS_BOUND = 0x11; - private static final int ANNOTATION_TARGET_TYPE_GENERIC_METHOD_BOUND = 0x12; - private static final int ANNOTATION_TARGET_TYPE_FIELD = 0x13; - private static final int ANNOTATION_TARGET_TYPE_RETURN = 0x14; - private static final int ANNOTATION_TARGET_TYPE_RECEIVER = 0x15; - private static final int ANNOTATION_TARGET_TYPE_FORMAL = 0x16; - private static final int ANNOTATION_TARGET_TYPE_THROWS = 0x17; - private static final int ANNOTATION_TARGET_TYPE_LOCAL_VARIABLE = 0x40; - private static final int ANNOTATION_TARGET_TYPE_RESOURCE_VARIABLE = 0x41; - private static final int ANNOTATION_TARGET_TYPE_EXCEPTION = 0x42; - private static final int ANNOTATION_TARGET_TYPE_INSTANCEOF = 0x43; - private static final int ANNOTATION_TARGET_TYPE_NEW = 0x44; - private static final int ANNOTATION_TARGET_TYPE_DOUBLE_COLON_NEW = 0x45; - private static final int ANNOTATION_TARGET_TYPE_DOUBLE_COLON_ID = 0x46; - private static final int ANNOTATION_TARGET_TYPE_CAST = 0x47; - private static final int ANNOTATION_TARGET_TYPE_INVOCATION_CONSTRUCTOR = 0x48; - private static final int ANNOTATION_TARGET_TYPE_INVOCATION_METHOD = 0x49; - private static final int ANNOTATION_TARGET_TYPE_GENERIC_DOUBLE_COLON_NEW = 0x4A; - private static final int ANNOTATION_TARGET_TYPE_GENERIC_DOUBLE_COLON_ID = 0x4B; - - private static final int ANNOTATION_TARGET_UNION_TYPE_PARAMETER = 1; - private static final int ANNOTATION_TARGET_UNION_SUPERTYPE = 2; - private static final int ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND = 3; - private static final int ANNOTATION_TARGET_UNION_EMPTY = 4; - private static final int ANNOTATION_TARGET_UNION_FORMAL_PARAMETER = 5; - private static final int ANNOTATION_TARGET_UNION_THROWS = 6; - private static final int ANNOTATION_TARGET_UNION_LOCAL_VAR = 7; - private static final int ANNOTATION_TARGET_UNION_CATCH = 8; - private static final int ANNOTATION_TARGET_UNION_OFFSET = 9; - private static final int ANNOTATION_TARGET_UNION_TYPE_ARGUMENT = 10; - - @SuppressWarnings("FieldCanBeLocal") private List locations; - @SuppressWarnings("FieldCanBeLocal") private List annotations; - - @Override - public void initContent(ConstantPool pool) throws IOException { - DataInputStream data = stream(); - - int len = data.readUnsignedByte(); - if (len > 0) { - locations = new ArrayList(len); - annotations = new ArrayList(len); - for (int i = 0; i < len; i++) { - locations.add(parseAnnotationLocation(data)); - annotations.add(StructAnnotationAttribute.parseAnnotation(data, pool)); - } - } - else { - locations = Collections.emptyList(); - annotations = Collections.emptyList(); - } - } - - private static AnnotationLocation parseAnnotationLocation(DataInputStream data) throws IOException { - AnnotationLocation ann_location = new AnnotationLocation(); - - // target type - ann_location.target_type = data.readUnsignedByte(); - - // target union - switch (ann_location.target_type) { - case ANNOTATION_TARGET_TYPE_GENERIC_CLASS: - case ANNOTATION_TARGET_TYPE_GENERIC_METHOD: - ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_PARAMETER; - break; - case ANNOTATION_TARGET_TYPE_EXTENDS_IMPLEMENTS: - ann_location.target_union = ANNOTATION_TARGET_UNION_SUPERTYPE; - break; - case ANNOTATION_TARGET_TYPE_GENERIC_CLASS_BOUND: - case ANNOTATION_TARGET_TYPE_GENERIC_METHOD_BOUND: - ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND; - break; - case ANNOTATION_TARGET_TYPE_FIELD: - case ANNOTATION_TARGET_TYPE_RETURN: - case ANNOTATION_TARGET_TYPE_RECEIVER: - ann_location.target_union = ANNOTATION_TARGET_UNION_EMPTY; - break; - case ANNOTATION_TARGET_TYPE_FORMAL: - ann_location.target_union = ANNOTATION_TARGET_UNION_FORMAL_PARAMETER; - break; - case ANNOTATION_TARGET_TYPE_THROWS: - ann_location.target_union = ANNOTATION_TARGET_UNION_THROWS; - break; - case ANNOTATION_TARGET_TYPE_LOCAL_VARIABLE: - case ANNOTATION_TARGET_TYPE_RESOURCE_VARIABLE: - ann_location.target_union = ANNOTATION_TARGET_UNION_LOCAL_VAR; - break; - case ANNOTATION_TARGET_TYPE_EXCEPTION: - ann_location.target_union = ANNOTATION_TARGET_UNION_CATCH; - break; - case ANNOTATION_TARGET_TYPE_INSTANCEOF: - case ANNOTATION_TARGET_TYPE_NEW: - case ANNOTATION_TARGET_TYPE_DOUBLE_COLON_NEW: - case ANNOTATION_TARGET_TYPE_DOUBLE_COLON_ID: - ann_location.target_union = ANNOTATION_TARGET_UNION_OFFSET; - break; - case ANNOTATION_TARGET_TYPE_CAST: - case ANNOTATION_TARGET_TYPE_INVOCATION_CONSTRUCTOR: - case ANNOTATION_TARGET_TYPE_INVOCATION_METHOD: - case ANNOTATION_TARGET_TYPE_GENERIC_DOUBLE_COLON_NEW: - case ANNOTATION_TARGET_TYPE_GENERIC_DOUBLE_COLON_ID: - ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_ARGUMENT; - break; - default: - throw new RuntimeException("Unknown target type in a type annotation!"); - } - - // target union data - - switch (ann_location.target_union) { - case ANNOTATION_TARGET_UNION_TYPE_PARAMETER: - case ANNOTATION_TARGET_UNION_FORMAL_PARAMETER: - ann_location.data = new int[]{data.readUnsignedByte()}; - break; - case ANNOTATION_TARGET_UNION_SUPERTYPE: - case ANNOTATION_TARGET_UNION_THROWS: - case ANNOTATION_TARGET_UNION_CATCH: - case ANNOTATION_TARGET_UNION_OFFSET: - ann_location.data = new int[]{data.readUnsignedShort()}; - break; - case ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND: - ann_location.data = new int[]{data.readUnsignedByte(), data.readUnsignedByte()}; - break; - case ANNOTATION_TARGET_UNION_EMPTY: - break; - case ANNOTATION_TARGET_UNION_LOCAL_VAR: - int table_length = data.readUnsignedShort(); - - ann_location.data = new int[table_length * 3 + 1]; - ann_location.data[0] = table_length; - - for (int i = 0; i < table_length; ++i) { - ann_location.data[3 * i + 1] = data.readUnsignedShort(); - ann_location.data[3 * i + 2] = data.readUnsignedShort(); - ann_location.data[3 * i + 3] = data.readUnsignedShort(); - } - break; - case ANNOTATION_TARGET_UNION_TYPE_ARGUMENT: - ann_location.data = new int[]{data.readUnsignedShort(), data.readUnsignedByte()}; - } - - // target path - int path_length = data.readUnsignedByte(); - - ann_location.target_path_kind = new int[path_length]; - ann_location.target_argument_index = new int[path_length]; - - for (int i = 0; i < path_length; ++i) { - ann_location.target_path_kind[i] = data.readUnsignedByte(); - ann_location.target_argument_index[i] = data.readUnsignedByte(); - } - - return ann_location; - } - - private static class AnnotationLocation { - public int target_type; - public int target_union; - public int[] data; - public int[] target_path_kind; - public int[] target_argument_index; - } -} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java index 840acc7..f97417e 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java @@ -1,45 +1,29 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.LinkConstant; import org.jetbrains.java.decompiler.struct.consts.PooledConstant; +import org.jetbrains.java.decompiler.util.DataInputFullStream; -import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class StructBootstrapMethodsAttribute extends StructGeneralAttribute { - private List methodRefs = new ArrayList(); - private List> methodArguments = new ArrayList>(); + private final List methodRefs = new ArrayList<>(); + private final List> methodArguments = new ArrayList<>(); @Override - public void initContent(ConstantPool pool) throws IOException { - DataInputStream data = stream(); - + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { int method_number = data.readUnsignedShort(); for (int i = 0; i < method_number; ++i) { int bootstrap_method_ref = data.readUnsignedShort(); int num_bootstrap_arguments = data.readUnsignedShort(); - List list_arguments = new ArrayList(); + List list_arguments = new ArrayList<>(); for (int j = 0; j < num_bootstrap_arguments; ++j) { int bootstrap_argument_ref = data.readUnsignedShort(); 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/StructConstantValueAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructConstantValueAttribute.java index 07a5073..a7e5cc1 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructConstantValueAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructConstantValueAttribute.java @@ -1,21 +1,8 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.IOException; @@ -24,8 +11,8 @@ public class StructConstantValueAttribute extends StructGeneralAttribute { private int index; @Override - public void initContent(ConstantPool pool) throws IOException { - index = stream().readUnsignedShort(); + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { + index = data.readUnsignedShort(); } public int getIndex() { diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java index c9b9ebb..a886b98 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java @@ -1,24 +1,10 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.LinkConstant; +import org.jetbrains.java.decompiler.util.DataInputFullStream; -import java.io.DataInputStream; import java.io.IOException; public class StructEnclosingMethodAttribute extends StructGeneralAttribute { @@ -28,8 +14,7 @@ public class StructEnclosingMethodAttribute extends StructGeneralAttribute { private String methodDescriptor; @Override - public void initContent(ConstantPool pool) throws IOException { - DataInputStream data = stream(); + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { int classIndex = data.readUnsignedShort(); int methodIndex = data.readUnsignedShort(); diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java index 5eda047..a39d902 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java @@ -1,23 +1,9 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; -import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -28,11 +14,10 @@ public class StructExceptionsAttribute extends StructGeneralAttribute { private List throwsExceptions; @Override - public void initContent(ConstantPool pool) throws IOException { - DataInputStream data = stream(); + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { int len = data.readUnsignedShort(); if (len > 0) { - throwsExceptions = new ArrayList(len); + throwsExceptions = new ArrayList<>(len); for (int i = 0; i < len; i++) { throwsExceptions.add(data.readUnsignedShort()); } @@ -43,7 +28,7 @@ public class StructExceptionsAttribute extends StructGeneralAttribute { } public String getExcClassname(int index, ConstantPool pool) { - return pool.getPrimitiveConstant(throwsExceptions.get(index).intValue()).getString(); + return pool.getPrimitiveConstant(throwsExceptions.get(index)).getString(); } public List getThrowsExceptions() { diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java index 2069cee..6239ee4 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -28,95 +14,97 @@ import java.io.IOException; } */ public class StructGeneralAttribute { + 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"); + public static final Key ATTRIBUTE_EXCEPTIONS = new Key<>("Exceptions"); + public static final Key ATTRIBUTE_ENCLOSING_METHOD = new Key<>("EnclosingMethod"); + public static final Key ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS = new Key<>("RuntimeVisibleAnnotations"); + public static final Key ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS = new Key<>("RuntimeInvisibleAnnotations"); + public static final Key ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = new Key<>("RuntimeVisibleParameterAnnotations"); + public static final Key ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = new Key<>("RuntimeInvisibleParameterAnnotations"); + public static final Key ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS = new Key<>("RuntimeVisibleTypeAnnotations"); + public static final Key ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = new Key<>("RuntimeInvisibleTypeAnnotations"); + public static final Key ATTRIBUTE_LOCAL_VARIABLE_TABLE = new Key<>("LocalVariableTable"); + public static final Key ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE = new Key<>("LocalVariableTypeTable"); + public static final Key ATTRIBUTE_CONSTANT_VALUE = new Key<>("ConstantValue"); + public static final Key ATTRIBUTE_BOOTSTRAP_METHODS = new Key<>("BootstrapMethods"); + public static final Key ATTRIBUTE_SYNTHETIC = new Key<>("Synthetic"); + 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 String ATTRIBUTE_CODE = "Code"; - public static final String ATTRIBUTE_INNER_CLASSES = "InnerClasses"; - public static final String ATTRIBUTE_SIGNATURE = "Signature"; - public static final String ATTRIBUTE_ANNOTATION_DEFAULT = "AnnotationDefault"; - public static final String ATTRIBUTE_EXCEPTIONS = "Exceptions"; - public static final String ATTRIBUTE_ENCLOSING_METHOD = "EnclosingMethod"; - public static final String ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; - public static final String ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; - public static final String ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; - public static final String ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations"; - public static final String ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations"; - public static final String ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = "RuntimeInvisibleTypeAnnotations"; - public static final String ATTRIBUTE_LOCAL_VARIABLE_TABLE = "LocalVariableTable"; - public static final String ATTRIBUTE_CONSTANT_VALUE = "ConstantValue"; - public static final String ATTRIBUTE_BOOTSTRAP_METHODS = "BootstrapMethods"; - public static final String ATTRIBUTE_SYNTHETIC = "Synthetic"; - public static final String ATTRIBUTE_DEPRECATED = "Deprecated"; - public static final String ATTRIBUTE_LINE_NUMBER_TABLE = "LineNumberTable"; + @SuppressWarnings("unused") + public static class Key { + public final String name; - private String name; - private byte[] info; + public Key(String name) { + this.name = name; + } + } public static StructGeneralAttribute createAttribute(String name) { - StructGeneralAttribute attr; - - if (ATTRIBUTE_INNER_CLASSES.equals(name)) { - attr = new StructInnerClassesAttribute(); + if (ATTRIBUTE_CODE.name.equals(name)) { + return new StructCodeAttribute(); } - else if (ATTRIBUTE_CONSTANT_VALUE.equals(name)) { - attr = new StructConstantValueAttribute(); + else if (ATTRIBUTE_INNER_CLASSES.name.equals(name)) { + return new StructInnerClassesAttribute(); } - else if (ATTRIBUTE_SIGNATURE.equals(name)) { - attr = new StructGenericSignatureAttribute(); + else if (ATTRIBUTE_CONSTANT_VALUE.name.equals(name)) { + return new StructConstantValueAttribute(); } - else if (ATTRIBUTE_ANNOTATION_DEFAULT.equals(name)) { - attr = new StructAnnDefaultAttribute(); + else if (ATTRIBUTE_SIGNATURE.name.equals(name)) { + return new StructGenericSignatureAttribute(); } - else if (ATTRIBUTE_EXCEPTIONS.equals(name)) { - attr = new StructExceptionsAttribute(); + else if (ATTRIBUTE_ANNOTATION_DEFAULT.name.equals(name)) { + return new StructAnnDefaultAttribute(); } - else if (ATTRIBUTE_ENCLOSING_METHOD.equals(name)) { - attr = new StructEnclosingMethodAttribute(); + else if (ATTRIBUTE_EXCEPTIONS.name.equals(name)) { + return new StructExceptionsAttribute(); } - else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.equals(name) || - ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.equals(name)) { - attr = new StructAnnotationAttribute(); + else if (ATTRIBUTE_ENCLOSING_METHOD.name.equals(name)) { + return new StructEnclosingMethodAttribute(); } - else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.equals(name) || - ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.equals(name)) { - attr = new StructAnnotationParameterAttribute(); + else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.name.equals(name)) { + return new StructAnnotationAttribute(); } - else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(name) || - ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(name)) { - attr = new StructAnnotationTypeAttribute(); + else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.name.equals(name)) { + return new StructAnnotationParameterAttribute(); } - else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.equals(name)) { - attr = new StructLocalVariableTableAttribute(); + else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.name.equals(name)) { + return new StructTypeAnnotationAttribute(); } - else if (ATTRIBUTE_BOOTSTRAP_METHODS.equals(name)) { - attr = new StructBootstrapMethodsAttribute(); + else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.name.equals(name)) { + return new StructLocalVariableTableAttribute(); } - else if (ATTRIBUTE_SYNTHETIC.equals(name) || - ATTRIBUTE_DEPRECATED.equals(name)) { - attr = new StructGeneralAttribute(); + else if (ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.name.equals(name)) { + return new StructLocalVariableTypeTableAttribute(); } - else if (ATTRIBUTE_LINE_NUMBER_TABLE.equals(name)) { - attr = new StructLineNumberTableAttribute(); + else if (ATTRIBUTE_BOOTSTRAP_METHODS.name.equals(name)) { + return new StructBootstrapMethodsAttribute(); + } + else if (ATTRIBUTE_SYNTHETIC.name.equals(name) || ATTRIBUTE_DEPRECATED.name.equals(name)) { + return new StructGeneralAttribute(); + } + else if (ATTRIBUTE_LINE_NUMBER_TABLE.name.equals(name)) { + return new StructLineNumberTableAttribute(); + } + else if (ATTRIBUTE_METHOD_PARAMETERS.name.equals(name)) { + return new StructMethodParametersAttribute(); + } + else if (ATTRIBUTE_MODULE.name.equals(name)) { + return new StructModuleAttribute(); + } + else if (ATTRIBUTE_RECORD.name.equals(name)) { + return new StructRecordAttribute(); } else { - // unsupported attribute - return null; + return null; // unsupported attribute } - - attr.name = name; - return attr; } - protected DataInputFullStream stream() { - return new DataInputFullStream(info); - } - - public void initContent(ConstantPool pool) throws IOException { } - - public void setInfo(byte[] info) { - this.info = info; - } - - public String getName() { - return name; - } + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java index 62c892f..d3de832 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java @@ -1,21 +1,8 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.IOException; @@ -24,8 +11,8 @@ public class StructGenericSignatureAttribute extends StructGeneralAttribute { private String signature; @Override - public void initContent(ConstantPool pool) throws IOException { - int index = stream().readUnsignedShort(); + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { + int index = data.readUnsignedShort(); signature = pool.getPrimitiveConstant(index).getString(); } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java index 5969487..238900d 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java @@ -1,72 +1,60 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; +import org.jetbrains.java.decompiler.util.DataInputFullStream; -import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class StructInnerClassesAttribute extends StructGeneralAttribute { + public static final class Entry { + public final int outerNameIdx; + public final int simpleNameIdx; + public final int accessFlags; + public final String innerName; + public final String enclosingName; + public final String simpleName; - private List classEntries; - private List stringEntries; + private Entry(int outerNameIdx, int simpleNameIdx, int accessFlags, String innerName, String enclosingName, String simpleName) { + this.outerNameIdx = outerNameIdx; + this.simpleNameIdx = simpleNameIdx; + this.accessFlags = accessFlags; + this.innerName = innerName; + this.enclosingName = enclosingName; + this.simpleName = simpleName; + } + } + + private List entries; @Override - public void initContent(ConstantPool pool) throws IOException { - DataInputStream data = stream(); - + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { int len = data.readUnsignedShort(); if (len > 0) { - classEntries = new ArrayList(len); - stringEntries = new ArrayList(len); + entries = new ArrayList<>(len); for (int i = 0; i < len; i++) { - int[] classEntry = new int[4]; - for (int j = 0; j < 4; j++) { - classEntry[j] = data.readUnsignedShort(); - } - classEntries.add(classEntry); + int innerNameIdx = data.readUnsignedShort(); + int outerNameIdx = data.readUnsignedShort(); + int simpleNameIdx = data.readUnsignedShort(); + int accessFlags = data.readUnsignedShort(); - // inner name, enclosing class, original simple name - String[] stringEntry = new String[3]; - stringEntry[0] = pool.getPrimitiveConstant(classEntry[0]).getString(); - if (classEntry[1] != 0) { - stringEntry[1] = pool.getPrimitiveConstant(classEntry[1]).getString(); - } - if (classEntry[2] != 0) { - stringEntry[2] = pool.getPrimitiveConstant(classEntry[2]).getString(); - } - stringEntries.add(stringEntry); + String innerName = pool.getPrimitiveConstant(innerNameIdx).getString(); + String outerName = outerNameIdx != 0 ? pool.getPrimitiveConstant(outerNameIdx).getString() : null; + String simpleName = simpleNameIdx != 0 ? pool.getPrimitiveConstant(simpleNameIdx).getString() : null; + + entries.add(new Entry(outerNameIdx, simpleNameIdx, accessFlags, innerName, outerName, simpleName)); } } else { - classEntries = Collections.emptyList(); - stringEntries = Collections.emptyList(); + entries = Collections.emptyList(); } } - public List getClassEntries() { - return classEntries; + public List getEntries() { + return entries; } - - public List getStringEntries() { - return stringEntries; - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLineNumberTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLineNumberTableAttribute.java index f35d3d9..e641b39 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructLineNumberTableAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLineNumberTableAttribute.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; @@ -33,9 +19,7 @@ public class StructLineNumberTableAttribute extends StructGeneralAttribute { private int[] myLineInfo = InterpreterUtil.EMPTY_INT_ARRAY; @Override - public void initContent(ConstantPool pool) throws IOException { - DataInputFullStream data = stream(); - + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { int len = data.readUnsignedShort() * 2; if (len > 0) { myLineInfo = new int[len]; @@ -49,16 +33,18 @@ public class StructLineNumberTableAttribute extends StructGeneralAttribute { } } - public int getFirstLine() { - return myLineInfo.length > 0 ? myLineInfo[1] : -1; - } - public int findLineNumber(int pc) { - for (int i = 0; i < myLineInfo.length; i += 2) { - if (pc >= myLineInfo[i]) { - return myLineInfo[i + 1]; + if (myLineInfo.length >= 2) { + for (int i = myLineInfo.length - 2; i >= 0; i -= 2) { + if (pc >= myLineInfo[i]) { + return myLineInfo[i + 1]; + } } } return -1; } -} + + public int[] getRawData() { + return myLineInfo; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java index 60e40c0..66eb593 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java @@ -1,27 +1,13 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; /* u2 local_variable_table_length; @@ -34,34 +20,70 @@ import java.util.Map; } */ public class StructLocalVariableTableAttribute extends StructGeneralAttribute { - - private Map mapVarNames = new HashMap(); + private List localVariables = Collections.emptyList(); @Override - public void initContent(ConstantPool pool) throws IOException { - DataInputFullStream data = stream(); - + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { int len = data.readUnsignedShort(); if (len > 0) { - mapVarNames = new HashMap(len); + localVariables = new ArrayList<>(len); + for (int i = 0; i < len; i++) { - data.discard(4); + int start_pc = data.readUnsignedShort(); + int length = data.readUnsignedShort(); int nameIndex = data.readUnsignedShort(); - data.discard(2); + int descriptorIndex = data.readUnsignedShort(); int varIndex = data.readUnsignedShort(); - mapVarNames.put(varIndex, pool.getPrimitiveConstant(nameIndex).getString()); + localVariables.add(new LocalVariable(start_pc, + length, + pool.getPrimitiveConstant(nameIndex).getString(), + pool.getPrimitiveConstant(descriptorIndex).getString(), + varIndex)); } } else { - mapVarNames = Collections.emptyMap(); + localVariables = Collections.emptyList(); } } - public void addLocalVariableTable(StructLocalVariableTableAttribute attr) { - mapVarNames.putAll(attr.getMapVarNames()); + public void add(StructLocalVariableTableAttribute attr) { + localVariables.addAll(attr.localVariables); } - public Map getMapVarNames() { - return mapVarNames; + public String getName(int index, int visibleOffset) { + return matchingVars(index, visibleOffset).map(v -> v.name).findFirst().orElse(null); + } + + public String getDescriptor(int index, int visibleOffset) { + return matchingVars(index, visibleOffset).map(v -> v.descriptor).findFirst().orElse(null); + } + + private Stream matchingVars(int index, int visibleOffset) { + return localVariables.stream() + .filter(v -> v.index == index && (visibleOffset >= v.start_pc && visibleOffset < v.start_pc + v.length)); + } + + public boolean containsName(String name) { + return localVariables.stream().anyMatch(v -> Objects.equals(v.name, name)); + } + + public Map getMapParamNames() { + return localVariables.stream().filter(v -> v.start_pc == 0).collect(Collectors.toMap(v -> v.index, v -> v.name, (n1, n2) -> n2)); + } + + private static final class LocalVariable { + final int start_pc; + final int length; + final String name; + final String descriptor; + final int index; + + private LocalVariable(int start_pc, int length, String name, String descriptor, int index) { + this.start_pc = start_pc; + this.length = length; + this.name = name; + this.descriptor = descriptor; + this.index = index; + } } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTypeTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTypeTableAttribute.java new file mode 100644 index 0000000..829c78e --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTypeTableAttribute.java @@ -0,0 +1,34 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.struct.attr; + +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; + +import java.io.IOException; + +/* + u2 local_variable_type_table_length; + { u2 start_pc; + u2 length; + u2 name_index; + u2 signature_index; + u2 index; + } local_variable_type_table[local_variable_type_table_length]; +*/ +public class StructLocalVariableTypeTableAttribute extends StructGeneralAttribute { + // store signature instead of descriptor + private final StructLocalVariableTableAttribute backingAttribute = new StructLocalVariableTableAttribute(); + + @Override + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { + backingAttribute.initContent(data, pool); + } + + public void add(StructLocalVariableTypeTableAttribute attr) { + backingAttribute.add(attr.backingAttribute); + } + + public String getSignature(int index, int visibleOffset) { + return backingAttribute.getDescriptor(index, visibleOffset); + } +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructMethodParametersAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructMethodParametersAttribute.java new file mode 100644 index 0000000..08133a9 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructMethodParametersAttribute.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ +package org.jetbrains.java.decompiler.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; + +/* + u1 parameters_count; + { u2 name_index; + u2 access_flags; + } parameters[parameters_count]; +*/ +public class StructMethodParametersAttribute extends StructGeneralAttribute { + private List myEntries; + + @Override + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { + int len = data.readUnsignedByte(); + List entries; + if (len > 0) { + entries = new ArrayList<>(len); + + for (int i = 0; i < len; i++) { + int nameIndex = data.readUnsignedShort(); + String name = nameIndex != 0 ? pool.getPrimitiveConstant(nameIndex).getString() : null; + int access_flags = data.readUnsignedShort(); + entries.add(new Entry(name, access_flags)); + } + } + else { + entries = Collections.emptyList(); + } + myEntries = Collections.unmodifiableList(entries); + } + + public List getEntries() { + return myEntries; + } + + public static class Entry { + public final String myName; + public final int myAccessFlags; + + public Entry(String name, int accessFlags) { + myName = name; + myAccessFlags = accessFlags; + } + } +} 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/attr/StructTypeAnnotationAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructTypeAnnotationAttribute.java new file mode 100644 index 0000000..a202940 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructTypeAnnotationAttribute.java @@ -0,0 +1,92 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.struct.attr; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.TypeAnnotation; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class StructTypeAnnotationAttribute extends StructGeneralAttribute { + private List annotations = Collections.emptyList(); + + @Override + public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { + int len = data.readUnsignedShort(); + if (len > 0) { + annotations = new ArrayList<>(len); + for (int i = 0; i < len; i++) { + annotations.add(parse(data, pool)); + } + } + else { + annotations = Collections.emptyList(); + } + } + + private static TypeAnnotation parse(DataInputStream data, ConstantPool pool) throws IOException { + int targetType = data.readUnsignedByte(); + int target = targetType << 24; + + switch (targetType) { + case TypeAnnotation.CLASS_TYPE_PARAMETER: + case TypeAnnotation.METHOD_TYPE_PARAMETER: + case TypeAnnotation.METHOD_PARAMETER: + target |= data.readUnsignedByte(); + break; + + case TypeAnnotation.SUPER_TYPE_REFERENCE: + case TypeAnnotation.CLASS_TYPE_PARAMETER_BOUND: + case TypeAnnotation.METHOD_TYPE_PARAMETER_BOUND: + case TypeAnnotation.THROWS_REFERENCE: + case TypeAnnotation.CATCH_CLAUSE: + case TypeAnnotation.EXPR_INSTANCEOF: + case TypeAnnotation.EXPR_NEW: + case TypeAnnotation.EXPR_CONSTRUCTOR_REF: + case TypeAnnotation.EXPR_METHOD_REF: + target |= data.readUnsignedShort(); + break; + + case TypeAnnotation.TYPE_ARG_CAST: + case TypeAnnotation.TYPE_ARG_CONSTRUCTOR_CALL: + case TypeAnnotation.TYPE_ARG_METHOD_CALL: + case TypeAnnotation.TYPE_ARG_CONSTRUCTOR_REF: + case TypeAnnotation.TYPE_ARG_METHOD_REF: + data.skipBytes(3); + break; + + case TypeAnnotation.LOCAL_VARIABLE: + case TypeAnnotation.RESOURCE_VARIABLE: + data.skipBytes(data.readUnsignedShort() * 6); + break; + + case TypeAnnotation.FIELD: + case TypeAnnotation.METHOD_RETURN_TYPE: + case TypeAnnotation.METHOD_RECEIVER: + break; + + default: + throw new RuntimeException("unknown target type: " + targetType); + } + + int pathLength = data.readUnsignedByte(); + byte[] path = null; + if (pathLength > 0) { + path = new byte[2 * pathLength]; + data.readFully(path); + } + + AnnotationExprent annotation = StructAnnotationAttribute.parseAnnotation(data, pool); + + return new TypeAnnotation(target, path, annotation); + } + + public List getAnnotations() { + return annotations; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java index d534e6d..3280038 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -20,26 +6,28 @@ import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.struct.gen.NewClassNameBuilder; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.BitSet; import java.util.List; -public class ConstantPool { - +@SuppressWarnings("AssignmentToForLoopParameter") +public class ConstantPool implements NewClassNameBuilder { public static final int FIELD = 1; public static final int METHOD = 2; - private List pool = new ArrayList(); - private PoolInterceptor interceptor; - + private final List pool; + private final PoolInterceptor interceptor; public ConstantPool(DataInputStream in) throws IOException { int size = in.readUnsignedShort(); - int[] pass = new int[size]; + pool = new ArrayList<>(size); + BitSet[] nextPass = {new BitSet(size), new BitSet(size), new BitSet(size)}; // first dummy constant pool.add(null); @@ -52,54 +40,61 @@ public class ConstantPool { case CodeConstants.CONSTANT_Utf8: pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Utf8, in.readUTF())); break; + case CodeConstants.CONSTANT_Integer: - pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Integer, new Integer(in.readInt()))); + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Integer, Integer.valueOf(in.readInt()))); break; + case CodeConstants.CONSTANT_Float: - pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Float, new Float(in.readFloat()))); + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Float, in.readFloat())); break; + case CodeConstants.CONSTANT_Long: - pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Long, new Long(in.readLong()))); + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Long, in.readLong())); pool.add(null); i++; break; + case CodeConstants.CONSTANT_Double: - pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Double, new Double(in.readDouble()))); + pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Double, in.readDouble())); pool.add(null); i++; break; + 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())); - pass[i] = 1; + nextPass[0].set(i); break; + + case CodeConstants.CONSTANT_NameAndType: + pool.add(new LinkConstant(tag, in.readUnsignedShort(), in.readUnsignedShort())); + nextPass[0].set(i); + break; + case CodeConstants.CONSTANT_Fieldref: case CodeConstants.CONSTANT_Methodref: case CodeConstants.CONSTANT_InterfaceMethodref: - case CodeConstants.CONSTANT_NameAndType: case CodeConstants.CONSTANT_InvokeDynamic: pool.add(new LinkConstant(tag, in.readUnsignedShort(), in.readUnsignedShort())); - if (tag == CodeConstants.CONSTANT_NameAndType) { - pass[i] = 1; - } - else { - pass[i] = 2; - } + nextPass[1].set(i); break; + case CodeConstants.CONSTANT_MethodHandle: pool.add(new LinkConstant(tag, in.readUnsignedByte(), in.readUnsignedShort())); - pass[i] = 3; + nextPass[2].set(i); break; } } // resolving complex pool elements - for (int passIndex = 1; passIndex <= 3; passIndex++) { - for (int i = 1; i < size; i++) { - if (pass[i] == passIndex) { - pool.get(i).resolveConstant(this); - } + for (BitSet pass : nextPass) { + int idx = 0; + while ((idx = pass.nextSetBit(idx + 1)) > 0) { + pool.get(idx).resolveConstant(this); } } @@ -115,6 +110,7 @@ public class ConstantPool { case CodeConstants.CONSTANT_Utf8: in.readUTF(); break; + case CodeConstants.CONSTANT_Integer: case CodeConstants.CONSTANT_Float: case CodeConstants.CONSTANT_Fieldref: @@ -124,38 +120,41 @@ public class ConstantPool { case CodeConstants.CONSTANT_InvokeDynamic: in.discard(4); break; + case CodeConstants.CONSTANT_Long: case CodeConstants.CONSTANT_Double: in.discard(8); i++; break; + case CodeConstants.CONSTANT_Class: case CodeConstants.CONSTANT_String: case CodeConstants.CONSTANT_MethodType: in.discard(2); break; + case CodeConstants.CONSTANT_MethodHandle: in.discard(3); } } } - public int size() { - return pool.size(); - } - public String[] getClassElement(int elementType, String className, int nameIndex, int descriptorIndex) { String elementName = ((PrimitiveConstant)getConstant(nameIndex)).getString(); String descriptor = ((PrimitiveConstant)getConstant(descriptorIndex)).getString(); if (interceptor != null) { - String newElement = interceptor.getName(className + " " + elementName + " " + descriptor); + String oldClassName = interceptor.getOldName(className); + if (oldClassName != null) { + className = oldClassName; + } + + String newElement = interceptor.getName(className + ' ' + elementName + ' ' + descriptor); if (newElement != null) { elementName = newElement.split(" ")[1]; } - int type = elementType == FIELD ? CodeConstants.CONSTANT_Fieldref : CodeConstants.CONSTANT_Methodref; - String newDescriptor = buildNewDescriptor(type, descriptor); + String newDescriptor = buildNewDescriptor(elementType == FIELD, descriptor); if (newDescriptor != null) { descriptor = newDescriptor; } @@ -191,9 +190,10 @@ public class ConstantPool { ln.type == CodeConstants.CONSTANT_Methodref || ln.type == CodeConstants.CONSTANT_InterfaceMethodref)) { String newClassName = buildNewClassname(ln.classname); - String newElement = interceptor.getName(ln.classname + " " + ln.elementname + " " + ln.descriptor); - String newDescriptor = buildNewDescriptor(ln.type, ln.descriptor); - + String newElement = interceptor.getName(ln.classname + ' ' + ln.elementname + ' ' + ln.descriptor); + String newDescriptor = buildNewDescriptor(ln.type == CodeConstants.CONSTANT_Fieldref, ln.descriptor); + //TODO: Fix newElement being null caused by ln.classname being a leaf class instead of the class that declared the field/method. + //See the comments of IDEA-137253 for more information. if (newClassName != null || newElement != null || newDescriptor != null) { String className = newClassName == null ? ln.classname : newClassName; String elementName = newElement == null ? ln.elementname : newElement.split(" ")[1]; @@ -205,77 +205,31 @@ public class ConstantPool { return ln; } - private String buildNewClassname(String className) { + @Override + public String buildNewClassname(String className) { VarType vt = new VarType(className, true); 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(";"); + if (vt.arrayDim > 0) { + buffer.append("[".repeat(vt.arrayDim)).append('L').append(newName).append(';'); } else { buffer.append(newName); } - return buffer.toString(); } return null; } - private String buildNewDescriptor(int type, String descriptor) { - boolean updated = false; - - if (type == CodeConstants.CONSTANT_Fieldref) { - FieldDescriptor fd = FieldDescriptor.parseDescriptor(descriptor); - - VarType fType = fd.type; - if (fType.type == CodeConstants.TYPE_OBJECT) { - String newClassName = buildNewClassname(fType.value); - if (newClassName != null) { - fType.value = newClassName; - updated = true; - } - } - - if (updated) { - return fd.getDescriptor(); - } + private String buildNewDescriptor(boolean isField, String descriptor) { + if (isField) { + return FieldDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this); } else { - MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor); - - // parameters - for (VarType paramType : md.params) { - if (paramType.type == CodeConstants.TYPE_OBJECT) { - String newClassName = buildNewClassname(paramType.value); - if (newClassName != null) { - paramType.value = newClassName; - updated = true; - } - } - } - - // return value - if (md.ret.type == CodeConstants.TYPE_OBJECT) { - String newClassName = buildNewClassname(md.ret.value); - if (newClassName != null) { - md.ret.value = newClassName; - updated = true; - } - } - - if (updated) { - return md.getDescriptor(); - } + return MethodDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this); } - - return null; } } diff --git a/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java b/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java index 71bf146..b38800d 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java @@ -1,55 +1,14 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.consts; -import java.io.DataOutputStream; -import java.io.IOException; - -/* - * NameAndType, FieldRef, MethodRef, InterfaceMethodref - * InvokeDynamic, MethodHandle - */ - public class LinkConstant extends PooledConstant { - - // ***************************************************************************** - // public fields - // ***************************************************************************** - public int index1, index2; - public String classname; - public String elementname; - public String descriptor; - public int paramCount = 0; - - public boolean isVoid = false; - - public boolean returnCategory2 = false; - - - // ***************************************************************************** - // constructors - // ***************************************************************************** - public LinkConstant(int type, String classname, String elementname, String descriptor) { - this.type = type; + super(type); this.classname = classname; this.elementname = elementname; this.descriptor = descriptor; @@ -58,18 +17,26 @@ public class LinkConstant extends PooledConstant { } public LinkConstant(int type, int index1, int index2) { - this.type = type; + super(type); this.index1 = index1; this.index2 = index2; } + private void initConstant() { + if (type == CONSTANT_Methodref || + type == CONSTANT_InterfaceMethodref || + type == CONSTANT_InvokeDynamic || + (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 + + "; type = " + type + "; classname = " + classname + "; elementname = " + elementname); + } + } + } - // ***************************************************************************** - // public methods - // ***************************************************************************** - + @Override public void resolveConstant(ConstantPool pool) { - if (type == CONSTANT_NameAndType) { elementname = pool.getPrimitiveConstant(index1).getString(); descriptor = pool.getPrimitiveConstant(index2).getString(); @@ -94,21 +61,10 @@ public class LinkConstant extends PooledConstant { initConstant(); } - public void writeToStream(DataOutputStream out) throws IOException { - out.writeByte(type); - if (type == CONSTANT_MethodHandle) { - out.writeByte(index1); - } - else { - out.writeShort(index1); - } - out.writeShort(index2); - } - - + @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof LinkConstant)) return false; + if (!(o instanceof LinkConstant)) return false; LinkConstant cn = (LinkConstant)o; return this.type == cn.type && @@ -116,50 +72,4 @@ public class LinkConstant extends PooledConstant { this.descriptor.equals(cn.descriptor) && (this.type != CONSTANT_NameAndType || this.classname.equals(cn.classname)); } - - // ***************************************************************************** - // private methods - // ***************************************************************************** - - private void initConstant() { - - if (type == CONSTANT_Methodref || - type == CONSTANT_InterfaceMethodref || - type == CONSTANT_InvokeDynamic || - type == CONSTANT_MethodHandle) { - resolveDescriptor(descriptor); - } - else if (type == CONSTANT_Fieldref) { - returnCategory2 = ("D".equals(descriptor) || "J".equals(descriptor)); - } - } - - private void resolveDescriptor(String descr) { - - String[] arr = descr.split("[()]"); - String par = arr[1]; - - int index = 0, counter = 0; - int len = par.length(); - - while (index < len) { - - char c = par.charAt(index); - if (c == 'L') { - index = par.indexOf(";", index); - } - else if (c == '[') { - index++; - continue; - } - - counter++; - index++; - } - - paramCount = counter; - isVoid = "V".equals(arr[2]); - returnCategory2 = ("D".equals(arr[2]) || "J".equals(arr[2])); - } -} - +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java b/src/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java index d3d30c0..7486fb3 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java @@ -1,117 +1,14 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.consts; import org.jetbrains.java.decompiler.code.CodeConstants; -import java.io.DataOutputStream; -import java.io.IOException; +public class PooledConstant implements CodeConstants { + public final int type; -/* - cp_info { - u1 tag; - u1 info[]; - } - -*/ - -public class PooledConstant implements CodeConstants, VariableTypeEnum { - - // ***************************************************************************** - // public fields - // ***************************************************************************** - - public int type; - - public boolean own = false; - - public int returnType; - - - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private Object[] values; - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public PooledConstant() { - } - - public PooledConstant(int type, Object[] values) { + public PooledConstant(int type) { this.type = type; - this.values = values; - this.returnType = poolTypeToIntern(type); } - public PooledConstant(int type, boolean own, Object[] values) { - this.type = type; - this.own = own; - this.values = values; - this.returnType = poolTypeToIntern(type); - } - - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public void resolveConstant(ConstantPool pool) { - // to be overwritten - } - - public void writeToStream(DataOutputStream out) throws IOException { - // to be overwritten - } - - public int poolTypeToIntern(int type) { - - switch (type) { - case CONSTANT_Integer: - return INT; - case CONSTANT_Float: - return FLOAT; - case CONSTANT_Long: - return LONG; - case CONSTANT_Double: - return DOUBLE; - case CONSTANT_String: - case CONSTANT_Class: // 1.5 -> ldc class - return REFERENCE; - default: - throw new RuntimeException("Huh?? What are you trying to load?"); - } - } - - public Object getValue(int index) { - return values[index]; - } - - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public Object[] getValues() { - return values; - } - - public void setValues(Object[] values) { - this.values = values; - } -} + public void resolveConstant(ConstantPool pool) { } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java b/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java index 4cd4dd8..408622a 100644 --- a/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java +++ b/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java @@ -1,126 +1,50 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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 java.io.DataOutputStream; -import java.io.IOException; - -/* - * Integer, Long, Float, Double, String, Class, UTF8 - */ - public class PrimitiveConstant extends PooledConstant { - - // ***************************************************************************** - // public fields - // ***************************************************************************** - public int index; - public Object value; - public boolean isArray; - // ***************************************************************************** - // constructors - // ***************************************************************************** - public PrimitiveConstant(int type, Object value) { - this.type = type; + super(type); this.value = value; initConstant(); } public PrimitiveConstant(int type, int index) { - this.type = type; + super(type); this.index = index; } - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public int getInt() { - return ((Integer)value).intValue(); - } - - public long getLong() { - return ((Long)value).longValue(); - } - - public float getFloat() { - return ((Float)value).floatValue(); - } - - public double getDouble() { - return ((Double)value).doubleValue(); + private void initConstant() { + if (type == CONSTANT_Class) { + String className = getString(); + isArray = (className.length() > 0 && className.charAt(0) == '['); // empty string for a class name seems to be possible in some android files + } } public String getString() { return (String)value; } + @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(); } } - public void writeToStream(DataOutputStream out) throws IOException { - - out.writeByte(type); - switch (type) { - case CONSTANT_Integer: - out.writeInt(getInt()); - break; - case CONSTANT_Float: - out.writeFloat(getFloat()); - break; - case CONSTANT_Long: - out.writeLong(getLong()); - break; - case CONSTANT_Double: - out.writeDouble(getDouble()); - break; - case CONSTANT_Utf8: - out.writeUTF(getString()); - break; - default: // CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType - out.writeShort(index); - } - } - + @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof PrimitiveConstant)) return false; + if (!(o instanceof PrimitiveConstant)) return false; PrimitiveConstant cn = (PrimitiveConstant)o; return this.type == cn.type && this.isArray == cn.isArray && this.value.equals(cn.value); } - - private void initConstant() { - if (type == CONSTANT_Class) { - String className = getString(); - isArray = - (className.length() > 0 && className.charAt(0) == '['); // empty string for a class name seems to be possible in some android files - } - } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/consts/VariableTypeEnum.java b/src/org/jetbrains/java/decompiler/struct/consts/VariableTypeEnum.java deleted file mode 100644 index 7950d5b..0000000 --- a/src/org/jetbrains/java/decompiler/struct/consts/VariableTypeEnum.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.struct.consts; - -public interface VariableTypeEnum { - - int BOOLEAN = 1; - int BYTE = 2; - int CHAR = 3; - int SHORT = 4; - int INT = 5; - int FLOAT = 6; - int LONG = 7; - int DOUBLE = 8; - int RETURN_ADDRESS = 9; - int REFERENCE = 10; - int INSTANCE_UNINITIALIZED = 11; - int VALUE_UNKNOWN = 12; - int VOID = 13; - - Integer BOOLEAN_OBJ = new Integer(BOOLEAN); - Integer BYTE_OBJ = new Integer(BYTE); - Integer CHAR_OBJ = new Integer(CHAR); - Integer SHORT_OBJ = new Integer(SHORT); - Integer INT_OBJ = new Integer(INT); - Integer FLOAT_OBJ = new Integer(FLOAT); - Integer LONG_OBJ = new Integer(LONG); - Integer DOUBLE_OBJ = new Integer(DOUBLE); - Integer RETURN_ADDRESS_OBJ = new Integer(RETURN_ADDRESS); - Integer REFERENCE_OBJ = new Integer(REFERENCE); - Integer INSTANCE_UNINITIALIZED_OBJ = new Integer(INSTANCE_UNINITIALIZED); - Integer VALUE_UNKNOWN_OBJ = new Integer(VALUE_UNKNOWN); - Integer VOID_OBJ = new Integer(VOID); -} diff --git a/src/org/jetbrains/java/decompiler/struct/gen/DataPoint.java b/src/org/jetbrains/java/decompiler/struct/gen/DataPoint.java index 65a6213..f3d8562 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/DataPoint.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/DataPoint.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.gen; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -24,9 +10,9 @@ import java.util.List; public class DataPoint { - private List localVariables = new ArrayList(); + private List localVariables = new ArrayList<>(); - private ListStack stack = new ListStack(); + private ListStack stack = new ListStack<>(); public void setVariable(int index, VarType value) { @@ -43,9 +29,6 @@ public class DataPoint { if (index < localVariables.size()) { return localVariables.get(index); } - else if (index < 0) { - throw new IndexOutOfBoundsException(); - } else { return new VarType(CodeConstants.TYPE_NOTINITIALIZED); } @@ -53,7 +36,7 @@ public class DataPoint { public DataPoint copy() { DataPoint point = new DataPoint(); - point.setLocalVariables(new ArrayList(localVariables)); + point.setLocalVariables(new ArrayList<>(localVariables)); point.setStack(stack.clone()); return point; } @@ -73,7 +56,7 @@ public class DataPoint { VarType var = md.params[i]; point.setVariable(k++, var); - if (var.stack_size == 2) { + if (var.stackSize == 2) { point.setVariable(k++, new VarType(CodeConstants.TYPE_GROUP2EMPTY)); } } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java index fd213f0..50d41ba 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java @@ -1,52 +1,42 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; -public class FieldDescriptor { +import org.jetbrains.java.decompiler.code.CodeConstants; + +public final class FieldDescriptor { public static final FieldDescriptor INTEGER_DESCRIPTOR = parseDescriptor("Ljava/lang/Integer;"); public static final FieldDescriptor LONG_DESCRIPTOR = parseDescriptor("Ljava/lang/Long;"); public static final FieldDescriptor FLOAT_DESCRIPTOR = parseDescriptor("Ljava/lang/Float;"); public static final FieldDescriptor DOUBLE_DESCRIPTOR = parseDescriptor("Ljava/lang/Double;"); - public VarType type; + public final VarType type; + public final String descriptorString; - public String descriptorString; - - private FieldDescriptor() { + private FieldDescriptor(String descriptor) { + type = new VarType(descriptor); + descriptorString = descriptor; } - public static FieldDescriptor parseDescriptor(String descr) { - - FieldDescriptor fd = new FieldDescriptor(); - - fd.type = new VarType(descr); - fd.descriptorString = descr; - - return fd; + public static FieldDescriptor parseDescriptor(String descriptor) { + return new FieldDescriptor(descriptor); } - public String getDescriptor() { - return type.toString(); + public String buildNewDescriptor(NewClassNameBuilder builder) { + if (type.type == CodeConstants.TYPE_OBJECT) { + String newClassName = builder.buildNewClassname(type.value); + if (newClassName != null) { + return new VarType(type.type, type.arrayDim, newClassName).toString(); + } + } + + return null; } @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof FieldDescriptor)) return false; + if (!(o instanceof FieldDescriptor)) return false; FieldDescriptor fd = (FieldDescriptor)o; return type.equals(fd.type); diff --git a/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java index 7dbc16c..2ad7c68 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java @@ -1,93 +1,119 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; + 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; - public VarType[] params; - - public VarType ret; - - - public static MethodDescriptor parseDescriptor(String mdescr) { - - MethodDescriptor md = new MethodDescriptor(); - - List lst = new ArrayList(); - String[] pars = mdescr.split("[()]"); - - String par = pars[1]; - - int indexFrom = -1, ind, index = 0; - int len = par.length(); - - for (; index < len; index++) { - - switch (par.charAt(index)) { - case '[': - if (indexFrom < 0) { - indexFrom = index; - } - break; - case 'L': - ind = par.indexOf(";", index); - lst.add(par.substring(indexFrom < 0 ? index : indexFrom, ind + 1)); - index = ind; - indexFrom = -1; - break; - default: - lst.add(par.substring(indexFrom < 0 ? index : indexFrom, index + 1)); - indexFrom = -1; - } - } - - lst.add(pars[2]); - - - md.params = new VarType[lst.size() - 1]; - - int i = 0; - for (; i < lst.size() - 1; i++) { - md.params[i] = new VarType(lst.get(i)); - } - md.ret = new VarType(lst.get(i)); - - return md; + private MethodDescriptor(VarType[] params, VarType ret) { + this.params = params; + this.ret = ret; } - public String getDescriptor() { - String res = "("; - - for (int j = 0; j < params.length; j++) { - res += params[j].toString(); + public static MethodDescriptor parseDescriptor(String descriptor) { + int parenth = descriptor.lastIndexOf(')'); + if (descriptor.length() < 2 || parenth < 0 || descriptor.charAt(0) != '(') { + throw new IllegalArgumentException("Invalid descriptor: " + descriptor); } - res += ")" + ret.toString(); + VarType[] params; - return res; + if (parenth > 1) { + String parameters = descriptor.substring(1, parenth); + List lst = new ArrayList<>(); + + int indexFrom = -1, ind, len = parameters.length(), index = 0; + while (index < len) { + switch (parameters.charAt(index)) { + case '[': + if (indexFrom < 0) { + indexFrom = index; + } + break; + case 'L': + ind = parameters.indexOf(";", index); + lst.add(parameters.substring(indexFrom < 0 ? index : indexFrom, ind + 1)); + index = ind; + indexFrom = -1; + break; + default: + lst.add(parameters.substring(indexFrom < 0 ? index : indexFrom, index + 1)); + indexFrom = -1; + } + index++; + } + + params = new VarType[lst.size()]; + for (int i = 0; i < lst.size(); i++) { + params[i] = new VarType(lst.get(i)); + } + } + else { + params = VarType.EMPTY_ARRAY; + } + + VarType ret = new VarType(descriptor.substring(parenth + 1)); + + return new MethodDescriptor(params, ret); + } + + public String buildNewDescriptor(NewClassNameBuilder builder) { + boolean updated = false; + + VarType[] newParams; + if (params.length > 0) { + newParams = params.clone(); + for (int i = 0; i < params.length; i++) { + VarType substitute = buildNewType(params[i], builder); + if (substitute != null) { + newParams[i] = substitute; + updated = true; + } + } + } + else { + newParams = VarType.EMPTY_ARRAY; + } + + VarType newRet = ret; + VarType substitute = buildNewType(ret, builder); + if (substitute != null) { + newRet = substitute; + updated = true; + } + + if (updated) { + StringBuilder res = new StringBuilder("("); + for (VarType param : newParams) { + res.append(param); + } + res.append(")").append(newRet.toString()); + return res.toString(); + } + + return null; + } + + private static VarType buildNewType(VarType type, NewClassNameBuilder builder) { + if (type.type == CodeConstants.TYPE_OBJECT) { + String newClassName = builder.buildNewClassname(type.value); + if (newClassName != null) { + return new VarType(type.type, type.arrayDim, newClassName); + } + } + return null; } @Override public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof MethodDescriptor)) return false; + if (!(o instanceof MethodDescriptor)) return false; MethodDescriptor md = (MethodDescriptor)o; return ret.equals(md.ret) && Arrays.equals(params, md.params); @@ -99,4 +125,4 @@ public class MethodDescriptor { result = 31 * result + params.length; return result; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/gen/NewClassNameBuilder.java b/src/org/jetbrains/java/decompiler/struct/gen/NewClassNameBuilder.java new file mode 100644 index 0000000..32b8a5e --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/gen/NewClassNameBuilder.java @@ -0,0 +1,6 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.struct.gen; + +public interface NewClassNameBuilder { + String buildNewClassname(String className); +} diff --git a/src/org/jetbrains/java/decompiler/struct/gen/VarType.java b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java index a58898a..bda05d3 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/VarType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/VarType.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.gen; import org.jetbrains.java.decompiler.code.CodeConstants; @@ -20,7 +6,7 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil; public class VarType { // TODO: optimize switch - public static final int FALSEBOOLEAN = 1; + public static final VarType[] EMPTY_ARRAY = {}; public static final VarType VARTYPE_UNKNOWN = new VarType(CodeConstants.TYPE_UNKNOWN); public static final VarType VARTYPE_INT = new VarType(CodeConstants.TYPE_INT); @@ -35,312 +21,84 @@ public class VarType { // TODO: optimize switch public static final VarType VARTYPE_SHORTCHAR = new VarType(CodeConstants.TYPE_SHORTCHAR); public static final VarType VARTYPE_NULL = new VarType(CodeConstants.TYPE_NULL, 0, null); - public static final VarType VARTYPE_GROUP2EMPTY = new VarType(CodeConstants.TYPE_GROUP2EMPTY); public static final VarType VARTYPE_STRING = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/String"); public static final VarType VARTYPE_CLASS = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Class"); public static final VarType VARTYPE_OBJECT = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Object"); + public static final VarType VARTYPE_INTEGER = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Integer"); + public static final VarType VARTYPE_CHARACTER = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Character"); + public static final VarType VARTYPE_BYTE_OBJ = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Byte"); + public static final VarType VARTYPE_SHORT_OBJ = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Short"); public static final VarType VARTYPE_VOID = new VarType(CodeConstants.TYPE_VOID); - public int type; - - public int type_family; - - public int arraydim; - - public String value; - - public int stack_size; - - public int convinfo; + public final int type; + public final int arrayDim; + public final String value; + public final int typeFamily; + public final int stackSize; + public final boolean falseBoolean; public VarType(int type) { + this(type, 0); + } + + public VarType(int type, int arrayDim) { + this(type, arrayDim, getChar(type)); + } + + public VarType(int type, int arrayDim, String value) { + this(type, arrayDim, value, getFamily(type, arrayDim), getStackSize(type, arrayDim), false); + } + + private VarType(int type, int arrayDim, String value, int typeFamily, int stackSize, boolean falseBoolean) { this.type = type; - this.arraydim = 0; - - value = getChar(type); - setStackSize(type); - setFamily(); - } - - public VarType(int type, int arraydim) { - this(type); - this.arraydim = arraydim; - setFamily(); - } - - public VarType(int type, int arraydim, String value) { - this(type); - this.arraydim = arraydim; + this.arrayDim = arrayDim; this.value = value; - setFamily(); + this.typeFamily = typeFamily; + this.stackSize = stackSize; + this.falseBoolean = falseBoolean; } - public VarType(String strtype) { - this(strtype, false); + public VarType(String signature) { + this(signature, false); } - public VarType(String strtype, boolean cltype) { - parseTypeString(strtype, cltype); - setStackSize(type); - setFamily(); - } + public VarType(String signature, boolean clType) { + int type = 0; + int arrayDim = 0; + String value = null; - public void decArrayDim() { - if (arraydim > 0) { - arraydim--; - setFamily(); - } - else { - // throw new RuntimeException("array dimension equals 0!"); FIXME: investigate this case - } - } - - public String toString() { - String res = ""; - - for (int i = 0; i < arraydim; i++) { - res += "["; - } - - if (type == CodeConstants.TYPE_OBJECT) { - res += "L" + value + ";"; - } - else { - res += value; - } - - return res; - } - - public VarType copy() { - VarType v = new VarType(type, arraydim, value); - v.convinfo = convinfo; - return v; - } - - public boolean isFalseBoolean() { - return (convinfo & FALSEBOOLEAN) != 0; - } - - public boolean isSuperset(VarType val) { - - return this.equals(val) || this.isStrictSuperset(val); - } - - public boolean isStrictSuperset(VarType val) { - - int valtype = val.type; - - if (valtype == CodeConstants.TYPE_UNKNOWN && type != CodeConstants.TYPE_UNKNOWN) { - return true; - } - - if (val.arraydim > 0) { - return this.equals(VARTYPE_OBJECT); - } - else if (arraydim > 0) { - return (valtype == CodeConstants.TYPE_NULL); - } - - boolean res = false; - - switch (type) { - case CodeConstants.TYPE_INT: - res |= (valtype == CodeConstants.TYPE_SHORT || - valtype == CodeConstants.TYPE_CHAR); - case CodeConstants.TYPE_SHORT: - res |= (valtype == CodeConstants.TYPE_BYTE); - case CodeConstants.TYPE_CHAR: - res |= (valtype == CodeConstants.TYPE_SHORTCHAR); - case CodeConstants.TYPE_BYTE: - case CodeConstants.TYPE_SHORTCHAR: - res |= (valtype == CodeConstants.TYPE_BYTECHAR); - case CodeConstants.TYPE_BYTECHAR: - res |= (valtype == CodeConstants.TYPE_BOOLEAN); - break; - case CodeConstants.TYPE_OBJECT: - if (valtype == CodeConstants.TYPE_NULL) { - return true; - } - else if (this.equals(VARTYPE_OBJECT)) { - return valtype == CodeConstants.TYPE_OBJECT && - !val.equals(VARTYPE_OBJECT); - } - } - - return res; - } - - // type1 and type2 must not be null - public static VarType getCommonMinType(VarType type1, VarType type2) { - - if (type1.type == CodeConstants.TYPE_BOOLEAN && type2.type == CodeConstants.TYPE_BOOLEAN) { // special case booleans - return type1.isFalseBoolean() ? type2 : type1; - } - - if (type1.isSuperset(type2)) { - return type2; - } - else if (type2.isSuperset(type1)) { - return type1; - } - else if (type1.type_family == type2.type_family) { - switch (type1.type_family) { - case CodeConstants.TYPE_FAMILY_INTEGER: - if ((type1.type == CodeConstants.TYPE_CHAR && type2.type == CodeConstants.TYPE_SHORT) - || (type1.type == CodeConstants.TYPE_SHORT && type2.type == CodeConstants.TYPE_CHAR)) { - return VARTYPE_SHORTCHAR; - } - else { - return VARTYPE_BYTECHAR; - } - case CodeConstants.TYPE_FAMILY_OBJECT: - return VARTYPE_NULL; - } - } - - return null; - } - - // type1 and type2 must not be null - public static VarType getCommonSupertype(VarType type1, VarType type2) { - - if (type1.type == CodeConstants.TYPE_BOOLEAN && type2.type == CodeConstants.TYPE_BOOLEAN) { // special case booleans - return type1.isFalseBoolean() ? type1 : type2; - } - - if (type1.isSuperset(type2)) { - return type1; - } - else if (type2.isSuperset(type1)) { - return type2; - } - else if (type1.type_family == type2.type_family) { - switch (type1.type_family) { - case CodeConstants.TYPE_FAMILY_INTEGER: - if ((type1.type == CodeConstants.TYPE_SHORTCHAR && type2.type == CodeConstants.TYPE_BYTE) - || (type1.type == CodeConstants.TYPE_BYTE && type2.type == CodeConstants.TYPE_SHORTCHAR)) { - return VARTYPE_SHORT; - } - else { - return VARTYPE_INT; - } - case CodeConstants.TYPE_FAMILY_OBJECT: - return VARTYPE_OBJECT; - } - } - - return null; - } - - public static VarType getMinTypeInFamily(int family) { - switch (family) { - case CodeConstants.TYPE_FAMILY_BOOLEAN: - return VARTYPE_BOOLEAN; - case CodeConstants.TYPE_FAMILY_INTEGER: - return VARTYPE_BYTECHAR; - case CodeConstants.TYPE_FAMILY_OBJECT: - return VARTYPE_NULL; - case CodeConstants.TYPE_FAMILY_FLOAT: - return VARTYPE_FLOAT; - case CodeConstants.TYPE_FAMILY_LONG: - return VARTYPE_LONG; - case CodeConstants.TYPE_FAMILY_DOUBLE: - return VARTYPE_DOUBLE; - case CodeConstants.TYPE_FAMILY_UNKNOWN: - return VARTYPE_UNKNOWN; - default: - throw new RuntimeException("invalid type family!"); - } - } - - public boolean equals(Object o) { - - if (o == this) { - return true; - } - - if (o == null || !(o instanceof VarType)) { - return false; - } - - VarType vt = (VarType)o; - return type == vt.type && arraydim == vt.arraydim && InterpreterUtil.equalObjects(value, vt.value); - } - - private void parseTypeString(String strtype, boolean cltype) { - - for (int i = 0; i < strtype.length(); i++) { - switch (strtype.charAt(i)) { + loop: + for (int i = 0; i < signature.length(); i++) { + switch (signature.charAt(i)) { case '[': - arraydim++; + arrayDim++; break; + case 'L': - if (strtype.charAt(strtype.length() - 1) == ';') { + if (signature.charAt(signature.length() - 1) == ';') { type = CodeConstants.TYPE_OBJECT; - value = strtype.substring(i + 1, strtype.length() - 1); - return; + value = signature.substring(i + 1, signature.length() - 1); + break loop; } + default: - value = strtype.substring(i, strtype.length()); - if ((cltype && i == 0) || value.length() > 1) { + value = signature.substring(i); + if ((clType && i == 0) || value.length() > 1) { type = CodeConstants.TYPE_OBJECT; } else { type = getType(value.charAt(0)); } - return; + break loop; } } - } - private void setStackSize(int type) { - if (arraydim > 0) { - stack_size = 1; - } - else { - stack_size = (type == CodeConstants.TYPE_DOUBLE || - type == CodeConstants.TYPE_LONG) ? 2 : - ((type == CodeConstants.TYPE_VOID || - type == CodeConstants.TYPE_GROUP2EMPTY) ? 0 : 1); - } - } - - private static int getType(char c) { - switch (c) { - case 'B': - return CodeConstants.TYPE_BYTE; - case 'C': - return CodeConstants.TYPE_CHAR; - case 'D': - return CodeConstants.TYPE_DOUBLE; - case 'F': - return CodeConstants.TYPE_FLOAT; - case 'I': - return CodeConstants.TYPE_INT; - case 'J': - return CodeConstants.TYPE_LONG; - case 'S': - return CodeConstants.TYPE_SHORT; - case 'Z': - return CodeConstants.TYPE_BOOLEAN; - case 'V': - return CodeConstants.TYPE_VOID; - case 'G': - return CodeConstants.TYPE_GROUP2EMPTY; - case 'N': - return CodeConstants.TYPE_NOTINITIALIZED; - case 'A': - return CodeConstants.TYPE_ADDRESS; - case 'X': - return CodeConstants.TYPE_BYTECHAR; - case 'Y': - return CodeConstants.TYPE_SHORTCHAR; - case 'U': - return CodeConstants.TYPE_UNKNOWN; - default: - throw new RuntimeException("Invalid type"); - } + this.type = type; + this.arrayDim = arrayDim; + this.value = value; + this.typeFamily = getFamily(type, arrayDim); + this.stackSize = getStackSize(type, arrayDim); + this.falseBoolean = false; } private static String getChar(int type) { @@ -383,11 +141,26 @@ public class VarType { // TODO: optimize switch } } - public void setFamily() { + private static int getStackSize(int type, int arrayDim) { + if (arrayDim > 0) { + return 1; + } - if (arraydim > 0) { - this.type_family = CodeConstants.TYPE_FAMILY_OBJECT; - return; + switch (type) { + case CodeConstants.TYPE_DOUBLE: + case CodeConstants.TYPE_LONG: + return 2; + case CodeConstants.TYPE_VOID: + case CodeConstants.TYPE_GROUP2EMPTY: + return 0; + default: + return 1; + } + } + + private static int getFamily(int type, int arrayDim) { + if (arrayDim > 0) { + return CodeConstants.TYPE_FAMILY_OBJECT; } switch (type) { @@ -397,26 +170,275 @@ public class VarType { // TODO: optimize switch case CodeConstants.TYPE_CHAR: case CodeConstants.TYPE_SHORT: case CodeConstants.TYPE_INT: - this.type_family = CodeConstants.TYPE_FAMILY_INTEGER; - break; + return CodeConstants.TYPE_FAMILY_INTEGER; case CodeConstants.TYPE_DOUBLE: - this.type_family = CodeConstants.TYPE_FAMILY_DOUBLE; - break; + return CodeConstants.TYPE_FAMILY_DOUBLE; case CodeConstants.TYPE_FLOAT: - this.type_family = CodeConstants.TYPE_FAMILY_FLOAT; - break; + return CodeConstants.TYPE_FAMILY_FLOAT; case CodeConstants.TYPE_LONG: - this.type_family = CodeConstants.TYPE_FAMILY_LONG; - break; + return CodeConstants.TYPE_FAMILY_LONG; case CodeConstants.TYPE_BOOLEAN: - this.type_family = CodeConstants.TYPE_FAMILY_BOOLEAN; - break; + return CodeConstants.TYPE_FAMILY_BOOLEAN; case CodeConstants.TYPE_NULL: case CodeConstants.TYPE_OBJECT: - this.type_family = CodeConstants.TYPE_FAMILY_OBJECT; - break; + return CodeConstants.TYPE_FAMILY_OBJECT; default: - this.type_family = CodeConstants.TYPE_FAMILY_UNKNOWN; + return CodeConstants.TYPE_FAMILY_UNKNOWN; + } + } + + public VarType decreaseArrayDim() { + if (arrayDim > 0) { + return new VarType(type, arrayDim - 1, value); + } + else { + //throw new RuntimeException("array dimension equals 0!"); FIXME: investigate this case + return this; + } + } + + public VarType resizeArrayDim(int newArrayDim) { + return new VarType(type, newArrayDim, value, typeFamily, stackSize, falseBoolean); + } + + public VarType copy() { + return copy(false); + } + + public VarType copy(boolean forceFalseBoolean) { + return new VarType(type, arrayDim, value, typeFamily, stackSize, falseBoolean || forceFalseBoolean); + } + + public boolean isFalseBoolean() { + return falseBoolean; + } + + public boolean isSuperset(VarType val) { + return this.equals(val) || this.isStrictSuperset(val); + } + + public boolean isStrictSuperset(VarType val) { + int valType = val.type; + + if (valType == CodeConstants.TYPE_UNKNOWN && type != CodeConstants.TYPE_UNKNOWN) { + return true; + } + + if (val.arrayDim > 0) { + return this.equals(VARTYPE_OBJECT); + } + else if (arrayDim > 0) { + return (valType == CodeConstants.TYPE_NULL); + } + + boolean res = false; + + switch (type) { + case CodeConstants.TYPE_INT: + res = (valType == CodeConstants.TYPE_SHORT || valType == CodeConstants.TYPE_CHAR); + case CodeConstants.TYPE_SHORT: + res |= (valType == CodeConstants.TYPE_BYTE); + case CodeConstants.TYPE_CHAR: + res |= (valType == CodeConstants.TYPE_SHORTCHAR); + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_SHORTCHAR: + res |= (valType == CodeConstants.TYPE_BYTECHAR); + case CodeConstants.TYPE_BYTECHAR: + res |= (valType == CodeConstants.TYPE_BOOLEAN); + break; + + case CodeConstants.TYPE_OBJECT: + if (valType == CodeConstants.TYPE_NULL) { + return true; + } + else if (this.equals(VARTYPE_OBJECT)) { + return valType == CodeConstants.TYPE_OBJECT && !val.equals(VARTYPE_OBJECT); + } + } + + return res; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof VarType)) { + return false; + } + + VarType vt = (VarType)o; + return type == vt.type && arrayDim == vt.arrayDim && InterpreterUtil.equalObjects(value, vt.value); + } + + @Override + public String toString() { + StringBuilder res = new StringBuilder(); + for (int i = 0; i < arrayDim; i++) { + res.append('['); + } + if (type == CodeConstants.TYPE_OBJECT) { + res.append('L').append(value).append(';'); + } + else { + res.append(value); + } + return res.toString(); + } + + // type1 and type2 must not be null + public static VarType getCommonMinType(VarType type1, VarType type2) { + if (type1.type == CodeConstants.TYPE_BOOLEAN && type2.type == CodeConstants.TYPE_BOOLEAN) { // special case booleans + return type1.isFalseBoolean() ? type2 : type1; + } + + if (type1.isSuperset(type2)) { + return type2; + } + else if (type2.isSuperset(type1)) { + return type1; + } + else if (type1.typeFamily == type2.typeFamily) { + switch (type1.typeFamily) { + case CodeConstants.TYPE_FAMILY_INTEGER: + if ((type1.type == CodeConstants.TYPE_CHAR && type2.type == CodeConstants.TYPE_SHORT) + || (type1.type == CodeConstants.TYPE_SHORT && type2.type == CodeConstants.TYPE_CHAR)) { + return VARTYPE_SHORTCHAR; + } + else { + return VARTYPE_BYTECHAR; + } + case CodeConstants.TYPE_FAMILY_OBJECT: + return VARTYPE_NULL; + } + } + + return null; + } + + // type1 and type2 must not be null + public static VarType getCommonSupertype(VarType type1, VarType type2) { + if (type1.type == CodeConstants.TYPE_BOOLEAN && type2.type == CodeConstants.TYPE_BOOLEAN) { // special case booleans + return type1.isFalseBoolean() ? type1 : type2; + } + + if (type1.isSuperset(type2)) { + return type1; + } + else if (type2.isSuperset(type1)) { + return type2; + } + else if (type1.typeFamily == type2.typeFamily) { + switch (type1.typeFamily) { + case CodeConstants.TYPE_FAMILY_INTEGER: + if ((type1.type == CodeConstants.TYPE_SHORTCHAR && type2.type == CodeConstants.TYPE_BYTE) + || (type1.type == CodeConstants.TYPE_BYTE && type2.type == CodeConstants.TYPE_SHORTCHAR)) { + return VARTYPE_SHORT; + } + else { + return VARTYPE_INT; + } + case CodeConstants.TYPE_FAMILY_OBJECT: + return VARTYPE_OBJECT; + } + } + + return null; + } + + public static VarType getMinTypeInFamily(int family) { + switch (family) { + case CodeConstants.TYPE_FAMILY_BOOLEAN: + return VARTYPE_BOOLEAN; + case CodeConstants.TYPE_FAMILY_INTEGER: + return VARTYPE_BYTECHAR; + case CodeConstants.TYPE_FAMILY_OBJECT: + return VARTYPE_NULL; + case CodeConstants.TYPE_FAMILY_FLOAT: + return VARTYPE_FLOAT; + case CodeConstants.TYPE_FAMILY_LONG: + return VARTYPE_LONG; + case CodeConstants.TYPE_FAMILY_DOUBLE: + return VARTYPE_DOUBLE; + case CodeConstants.TYPE_FAMILY_UNKNOWN: + return VARTYPE_UNKNOWN; + default: + throw new IllegalArgumentException("Invalid type family: " + family); + } + } + + public static int getType(char c) { + switch (c) { + case 'B': + return CodeConstants.TYPE_BYTE; + case 'C': + return CodeConstants.TYPE_CHAR; + case 'D': + return CodeConstants.TYPE_DOUBLE; + case 'F': + return CodeConstants.TYPE_FLOAT; + case 'I': + return CodeConstants.TYPE_INT; + case 'J': + return CodeConstants.TYPE_LONG; + case 'S': + return CodeConstants.TYPE_SHORT; + case 'Z': + return CodeConstants.TYPE_BOOLEAN; + case 'V': + return CodeConstants.TYPE_VOID; + case 'G': + return CodeConstants.TYPE_GROUP2EMPTY; + case 'N': + return CodeConstants.TYPE_NOTINITIALIZED; + case 'A': + return CodeConstants.TYPE_ADDRESS; + case 'X': + return CodeConstants.TYPE_BYTECHAR; + case 'Y': + return CodeConstants.TYPE_SHORTCHAR; + case 'U': + return CodeConstants.TYPE_UNKNOWN; + default: + throw new IllegalArgumentException("Invalid type: " + c); + } + } + public static char getTypeReverse(VarType c) { + switch (c.type) { + case CodeConstants.TYPE_BYTE: + return 'B'; + case CodeConstants.TYPE_CHAR: + return 'C'; + case CodeConstants.TYPE_DOUBLE: + return 'D'; + case CodeConstants.TYPE_FLOAT: + return 'F'; + case CodeConstants.TYPE_INT: + return 'I'; + case CodeConstants.TYPE_LONG: + return 'J'; + case CodeConstants.TYPE_SHORT: + return 'S'; + case CodeConstants.TYPE_BOOLEAN: + return 'Z'; + case CodeConstants.TYPE_VOID: + return 'V'; + case CodeConstants.TYPE_GROUP2EMPTY: + return 'G'; + case CodeConstants.TYPE_NOTINITIALIZED: + return 'N'; + case CodeConstants.TYPE_ADDRESS: + return 'A'; + case CodeConstants.TYPE_BYTECHAR: + return 'X'; + case CodeConstants.TYPE_SHORTCHAR: + return 'Y'; + case CodeConstants.TYPE_UNKNOWN: + return 'U'; + default: + throw new IllegalArgumentException("Invalid type: " + c); } } } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java index 66fab96..ad623a2 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.gen.generics; import java.util.ArrayList; @@ -22,9 +8,9 @@ public class GenericClassDescriptor { public GenericType superclass; - public List superinterfaces = new ArrayList(); + public final List superinterfaces = new ArrayList<>(); - public List fparameters = new ArrayList(); + public final List fparameters = new ArrayList<>(); - public List> fbounds = new ArrayList>(); + public final List> fbounds = new ArrayList<>(); } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java index 598d17b..a651817 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java @@ -1,21 +1,10 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.gen.generics; public class GenericFieldDescriptor { + public final GenericType type; - public GenericType type; -} + public GenericFieldDescriptor(GenericType type) { + this.type = type; + } +} \ No newline at end of file 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 3082eca..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,29 +1,15 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; -import org.jetbrains.java.decompiler.struct.StructClass; +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", @@ -63,9 +49,7 @@ public class GenericMain { public static GenericFieldDescriptor parseFieldSignature(String signature) { try { - GenericFieldDescriptor descriptor = new GenericFieldDescriptor(); - descriptor.type = new GenericType(signature); - return descriptor; + return new GenericFieldDescriptor(new GenericType(signature)); } catch (RuntimeException e) { DecompilerContext.getLogger().writeMessage("Invalid signature: " + signature, IFernflowerLogger.Severity.WARN); @@ -76,33 +60,34 @@ public class GenericMain { public static GenericMethodDescriptor parseMethodSignature(String signature) { String original = signature; try { - GenericMethodDescriptor descriptor = new GenericMethodDescriptor(); - - signature = parseFormalParameters(signature, descriptor.fparameters, descriptor.fbounds); + List typeParameters = new ArrayList<>(); + List> typeParameterBounds = new ArrayList<>(); + signature = parseFormalParameters(signature, typeParameters, typeParameterBounds); int to = signature.indexOf(")"); - String pars = signature.substring(1, to); + String parameters = signature.substring(1, to); signature = signature.substring(to + 1); - while (pars.length() > 0) { - String par = GenericType.getNextType(pars); - descriptor.params.add(new GenericType(par)); - pars = pars.substring(par.length()); + List parameterTypes = new ArrayList<>(); + while (parameters.length() > 0) { + String par = GenericType.getNextType(parameters); + parameterTypes.add(new GenericType(par)); + parameters = parameters.substring(par.length()); } - String par = GenericType.getNextType(signature); - descriptor.ret = new GenericType(par); - signature = signature.substring(par.length()); + String ret = GenericType.getNextType(signature); + GenericType returnType = new GenericType(ret); + signature = signature.substring(ret.length()); + List exceptionTypes = new ArrayList<>(); if (signature.length() > 0) { String[] exceptions = signature.split("\\^"); - for (int i = 1; i < exceptions.length; i++) { - descriptor.exceptions.add(new GenericType(exceptions[i])); + exceptionTypes.add(new GenericType(exceptions[i])); } } - return descriptor; + return new GenericMethodDescriptor(typeParameters, typeParameterBounds, parameterTypes, returnType, exceptionTypes); } catch (RuntimeException e) { DecompilerContext.getLogger().writeMessage("Invalid signature: " + original, IFernflowerLogger.Severity.WARN); @@ -110,7 +95,7 @@ public class GenericMain { } } - private static String parseFormalParameters(String signature, List parameters, List> bounds) { + private static String parseFormalParameters(String signature, List parameters, List> bounds) { if (signature.charAt(0) != '<') { return signature; } @@ -143,7 +128,7 @@ public class GenericMain { String param = value.substring(0, to); value = value.substring(to + 1); - List lstBounds = new ArrayList(); + List lstBounds = new ArrayList<>(); while (true) { if (value.charAt(0) == ':') { @@ -172,12 +157,9 @@ public class GenericMain { } public static String getGenericCastTypeName(GenericType type) { - String s = getTypeName(type); - int dim = type.arraydim; - while (dim-- > 0) { - s += "[]"; - } - return s; + StringBuilder s = new StringBuilder(getTypeName(type)); + TextUtil.append(s, "[]", type.arrayDim); + return s.toString(); } private static String getTypeName(GenericType type) { @@ -193,57 +175,68 @@ public class GenericMain { } else if (tp == CodeConstants.TYPE_OBJECT) { StringBuilder buffer = new StringBuilder(); - buffer.append(DecompilerContext.getImportCollector().getShortName(buildJavaClassName(type))); - - if (!type.getArguments().isEmpty()) { - buffer.append("<"); - for (int i = 0; i < type.getArguments().size(); i++) { - if (i > 0) { - buffer.append(", "); - } - int wildcard = type.getWildcards().get(i); - if (wildcard != GenericType.WILDCARD_NO) { - buffer.append("?"); - - switch (wildcard) { - case GenericType.WILDCARD_EXTENDS: - buffer.append(" extends "); - break; - case GenericType.WILDCARD_SUPER: - buffer.append(" super "); - } - } - - GenericType genPar = type.getArguments().get(i); - if (genPar != null) { - buffer.append(getGenericCastTypeName(genPar)); - } - } - buffer.append(">"); - } - + appendClassName(type, buffer); return buffer.toString(); } throw new RuntimeException("Invalid type: " + type); } - private static String buildJavaClassName(GenericType type) { - String name = ""; - for (GenericType tp : type.getEnclosingClasses()) { - name += tp.value + "$"; + private static void appendClassName(GenericType type, StringBuilder buffer) { + List enclosingClasses = type.getEnclosingClasses(); + + if (enclosingClasses.isEmpty()) { + String name = type.value.replace('/', '.'); + buffer.append(DecompilerContext.getImportCollector().getShortName(name)); } - name += type.value; + else { + for (GenericType tp : enclosingClasses) { + if (buffer.length() == 0) { + buffer.append(DecompilerContext.getImportCollector().getShortName(tp.value.replace('/', '.'))); + } + else { + buffer.append(tp.value); + } - String res = name.replace('/', '.'); - - if (res.contains("$")) { - StructClass cl = DecompilerContext.getStructContext().getClass(name); - if (cl == null || !cl.isOwn()) { - res = res.replace('$', '.'); + appendTypeArguments(tp, buffer); + buffer.append('.'); } + + buffer.append(type.value); } - return res; + appendTypeArguments(type, buffer); + } + + private static void appendTypeArguments(GenericType type, StringBuilder buffer) { + if (!type.getArguments().isEmpty()) { + buffer.append('<'); + + for (int i = 0; i < type.getArguments().size(); i++) { + if (i > 0) { + buffer.append(", "); + } + + int wildcard = type.getWildcards().get(i); + switch (wildcard) { + case GenericType.WILDCARD_UNBOUND: + buffer.append('?'); + break; + case GenericType.WILDCARD_EXTENDS: + buffer.append("? extends "); + break; + case GenericType.WILDCARD_SUPER: + buffer.append("? super "); + break; + } + + GenericType genPar = type.getArguments().get(i); + if (genPar != null) { + buffer.append(getGenericCastTypeName(genPar)); + } + } + + buffer.append(">"); + } } } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java index 7b4c9dc..eb80491 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java @@ -1,32 +1,29 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.gen.generics; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class GenericMethodDescriptor { + public final List typeParameters; + public final List> typeParameterBounds; + public final List parameterTypes; + public final GenericType returnType; + public final List exceptionTypes; - public List fparameters = new ArrayList(); + public GenericMethodDescriptor(List typeParameters, + List> typeParameterBounds, + List parameterTypes, + GenericType returnType, + List exceptionTypes) { + this.typeParameters = substitute(typeParameters); + this.typeParameterBounds = substitute(typeParameterBounds); + this.parameterTypes = substitute(parameterTypes); + this.returnType = returnType; + this.exceptionTypes = substitute(exceptionTypes); + } - public List> fbounds = new ArrayList>(); - - public List params = new ArrayList(); - - public GenericType ret; - - public List exceptions = new ArrayList(); -} + private static List substitute(List list) { + return list.isEmpty() ? Collections.emptyList() : list; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java index 160a0ac..60bffdb 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java @@ -1,21 +1,8 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.struct.gen.generics; import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.struct.gen.VarType; import java.util.ArrayList; import java.util.List; @@ -27,66 +14,66 @@ public class GenericType { public static final int WILDCARD_UNBOUND = 3; public static final int WILDCARD_NO = 4; - public int type; + public final int type; + public final int arrayDim; + public final String value; - public int arraydim; + private final List enclosingClasses = new ArrayList<>(); + private final List arguments = new ArrayList<>(); + private final List wildcards = new ArrayList<>(); - public String value; - - - private List enclosingClasses = new ArrayList(); - - private List arguments = new ArrayList(); - - private List wildcards = new ArrayList(); - - - public GenericType(int type, int arraydim, String value) { + public GenericType(int type, int arrayDim, String value) { this.type = type; - this.arraydim = arraydim; + this.arrayDim = arrayDim; this.value = value; } - - public GenericType(String strtype) { - - parseSignature(strtype); + private GenericType(GenericType other, int arrayDim) { + this(other.type, arrayDim, other.value); + enclosingClasses.addAll(other.enclosingClasses); + arguments.addAll(other.arguments); + wildcards.addAll(other.wildcards); } - private void parseSignature(String sig) { + public GenericType(String signature) { + int type = 0; + int arrayDim = 0; + String value = null; int index = 0; - while (index < sig.length()) { - - switch (sig.charAt(index)) { + loop: + while (index < signature.length()) { + switch (signature.charAt(index)) { case '[': - arraydim++; + arrayDim++; break; + case 'T': type = CodeConstants.TYPE_GENVAR; - value = sig.substring(index + 1, sig.length() - 1); - return; + value = signature.substring(index + 1, signature.length() - 1); + break loop; + case 'L': type = CodeConstants.TYPE_OBJECT; - sig = sig.substring(index + 1, sig.length() - 1); + signature = signature.substring(index + 1, signature.length() - 1); while (true) { - String cl = getNextClassSignature(sig); + String cl = getNextClassSignature(signature); String name = cl; String args = null; - int argfrom = cl.indexOf("<"); - if (argfrom >= 0) { - name = cl.substring(0, argfrom); - args = cl.substring(argfrom + 1, cl.length() - 1); + int argStart = cl.indexOf("<"); + if (argStart >= 0) { + name = cl.substring(0, argStart); + args = cl.substring(argStart + 1, cl.length() - 1); } - if (cl.length() < sig.length()) { - sig = sig.substring(cl.length() + 1); // skip '.' - GenericType type = new GenericType(CodeConstants.TYPE_OBJECT, 0, name); - parseArgumentsList(args, type); - enclosingClasses.add(type); + if (cl.length() < signature.length()) { + signature = signature.substring(cl.length() + 1); // skip '.' + GenericType type11 = new GenericType(CodeConstants.TYPE_OBJECT, 0, name); + parseArgumentsList(args, type11); + enclosingClasses.add(type11); } else { value = name; @@ -95,18 +82,22 @@ public class GenericType { } } - return; + break loop; + default: - value = sig.substring(index, index + 1); - type = getType(value.charAt(0)); + value = signature.substring(index, index + 1); + type = VarType.getType(value.charAt(0)); } index++; } + + this.type = type; + this.arrayDim = arrayDim; + this.value = value; } private static String getNextClassSignature(String value) { - int counter = 0; int index = 0; @@ -132,18 +123,16 @@ public class GenericType { } private static void parseArgumentsList(String value, GenericType type) { - if (value == null) { return; } while (value.length() > 0) { - - String tstr = getNextType(value); - int len = tstr.length(); + String typeStr = getNextType(value); + int len = typeStr.length(); int wildcard = WILDCARD_NO; - switch (tstr.charAt(0)) { + switch (typeStr.charAt(0)) { case '*': wildcard = WILDCARD_UNBOUND; break; @@ -158,41 +147,40 @@ public class GenericType { type.getWildcards().add(wildcard); if (wildcard != WILDCARD_NO) { - tstr = tstr.substring(1); + typeStr = typeStr.substring(1); } - type.getArguments().add(tstr.length() == 0 ? null : new GenericType(tstr)); + type.getArguments().add(typeStr.length() == 0 ? null : new GenericType(typeStr)); value = value.substring(len); } } public static String getNextType(String value) { - int counter = 0; int index = 0; - boolean contmode = false; + boolean contMode = false; loop: while (index < value.length()) { switch (value.charAt(index)) { case '*': - if (!contmode) { + if (!contMode) { break loop; } break; case 'L': case 'T': - if (!contmode) { - contmode = true; + if (!contMode) { + contMode = true; } case '[': case '+': case '-': break; default: - if (!contmode) { + if (!contMode) { break loop; } break; @@ -214,54 +202,19 @@ public class GenericType { return value.substring(0, index + 1); } - private static int getType(char c) { - switch (c) { - case 'B': - return CodeConstants.TYPE_BYTE; - case 'C': - return CodeConstants.TYPE_CHAR; - case 'D': - return CodeConstants.TYPE_DOUBLE; - case 'F': - return CodeConstants.TYPE_FLOAT; - case 'I': - return CodeConstants.TYPE_INT; - case 'J': - return CodeConstants.TYPE_LONG; - case 'S': - return CodeConstants.TYPE_SHORT; - case 'Z': - return CodeConstants.TYPE_BOOLEAN; - case 'V': - return CodeConstants.TYPE_VOID; - case 'G': - return CodeConstants.TYPE_GROUP2EMPTY; - case 'N': - return CodeConstants.TYPE_NOTINITIALIZED; - case 'A': - return CodeConstants.TYPE_ADDRESS; - case 'X': - return CodeConstants.TYPE_BYTECHAR; - case 'Y': - return CodeConstants.TYPE_SHORTCHAR; - case 'U': - return CodeConstants.TYPE_UNKNOWN; - default: - throw new RuntimeException("Invalid type"); - } + public GenericType decreaseArrayDim() { + assert arrayDim > 0 : this; + return new GenericType(this, arrayDim - 1); } - public List getArguments() { return arguments; } - public List getEnclosingClasses() { return enclosingClasses; } - public List getWildcards() { return wildcards; } diff --git a/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java index 70254d8..8b6105d 100644 --- a/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java +++ b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java @@ -1,21 +1,8 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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; @@ -26,55 +13,47 @@ import java.util.HashMap; import java.util.Map; public class LazyLoader { - - private final Map mapClassLinks = new HashMap(); + private final Map mapClassLinks = new HashMap<>(); private final IBytecodeProvider provider; public LazyLoader(IBytecodeProvider provider) { 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); - if (in == null) return null; - - try { + public ConstantPool loadPool(String className) { + try (DataInputFullStream in = getClassStream(className)) { + if (in != null) { in.discard(8); return new ConstantPool(in); } - finally { - in.close(); - } + + return null; } catch (IOException ex) { throw new RuntimeException(ex); } } - 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) return null; - - try { + 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); } @@ -112,23 +91,19 @@ public class LazyLoader { for (int j = 0; j < attrSize; j++) { int attrNameIndex = in.readUnsignedShort(); String attrName = pool.getPrimitiveConstant(attrNameIndex).getString(); - if (!StructGeneralAttribute.ATTRIBUTE_CODE.equals(attrName)) { + if (!StructGeneralAttribute.ATTRIBUTE_CODE.name.equals(attrName)) { in.discard(in.readInt()); continue; } in.discard(12); - byte[] code = new byte[codeFullLength]; - in.readFull(code); - return code; + + return in.read(codeFullLength); } break; } } - finally { - in.close(); - } return null; } @@ -155,17 +130,11 @@ public class LazyLoader { } } - public static class Link { - public static final int CLASS = 1; - public static final int ENTRY = 2; + public final String externalPath; + public final String internalPath; - public int type; - public String externalPath; - public String internalPath; - - public Link(int type, String externalPath, String internalPath) { - this.type = type; + public Link(String externalPath, String internalPath) { this.externalPath = externalPath; this.internalPath = internalPath; } diff --git a/src/org/jetbrains/java/decompiler/struct/match/IMatchable.java b/src/org/jetbrains/java/decompiler/struct/match/IMatchable.java new file mode 100644 index 0000000..7cc7cf9 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/match/IMatchable.java @@ -0,0 +1,30 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.struct.match; + +public interface IMatchable { + enum MatchProperties { + STATEMENT_TYPE, + STATEMENT_RET, + STATEMENT_STATSIZE, + STATEMENT_EXPRSIZE, + STATEMENT_POSITION, + STATEMENT_IFTYPE, + + EXPRENT_TYPE, + EXPRENT_RET, + EXPRENT_POSITION, + EXPRENT_FUNCTYPE, + EXPRENT_EXITTYPE, + EXPRENT_CONSTTYPE, + EXPRENT_CONSTVALUE, + EXPRENT_INVOCATION_CLASS, + EXPRENT_INVOCATION_SIGNATURE, + EXPRENT_INVOCATION_PARAMETER, + EXPRENT_VAR_INDEX, + EXPRENT_FIELD_NAME, + } + + IMatchable findObject(MatchNode matchNode, int index); + + boolean match(MatchNode matchNode, MatchEngine engine); +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/match/MatchEngine.java b/src/org/jetbrains/java/decompiler/struct/match/MatchEngine.java new file mode 100644 index 0000000..0eebccf --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/match/MatchEngine.java @@ -0,0 +1,226 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.struct.match; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties; +import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue; + +import java.util.*; + +public class MatchEngine { + private static final Map stat_properties = new HashMap<>(); + private static final Map expr_properties = new HashMap<>(); + private static final Map stat_type = new HashMap<>(); + private static final Map expr_type = new HashMap<>(); + private static final Map expr_func_type = new HashMap<>(); + private static final Map expr_exit_type = new HashMap<>(); + private static final Map stat_if_type = new HashMap<>(); + private static final Map expr_const_type = new HashMap<>(); + + static { + stat_properties.put("type", MatchProperties.STATEMENT_TYPE); + stat_properties.put("ret", MatchProperties.STATEMENT_RET); + stat_properties.put("position", MatchProperties.STATEMENT_POSITION); + stat_properties.put("statsize", MatchProperties.STATEMENT_STATSIZE); + stat_properties.put("exprsize", MatchProperties.STATEMENT_EXPRSIZE); + stat_properties.put("iftype", MatchProperties.STATEMENT_IFTYPE); + + expr_properties.put("type", MatchProperties.EXPRENT_TYPE); + expr_properties.put("ret", MatchProperties.EXPRENT_RET); + expr_properties.put("position", MatchProperties.EXPRENT_POSITION); + expr_properties.put("functype", MatchProperties.EXPRENT_FUNCTYPE); + expr_properties.put("exittype", MatchProperties.EXPRENT_EXITTYPE); + expr_properties.put("consttype", MatchProperties.EXPRENT_CONSTTYPE); + expr_properties.put("constvalue", MatchProperties.EXPRENT_CONSTVALUE); + expr_properties.put("invclass", MatchProperties.EXPRENT_INVOCATION_CLASS); + expr_properties.put("signature", MatchProperties.EXPRENT_INVOCATION_SIGNATURE); + expr_properties.put("parameter", MatchProperties.EXPRENT_INVOCATION_PARAMETER); + expr_properties.put("index", MatchProperties.EXPRENT_VAR_INDEX); + expr_properties.put("name", MatchProperties.EXPRENT_FIELD_NAME); + + stat_type.put("if", Statement.TYPE_IF); + stat_type.put("do", Statement.TYPE_DO); + stat_type.put("switch", Statement.TYPE_SWITCH); + stat_type.put("trycatch", Statement.TYPE_TRYCATCH); + stat_type.put("basicblock", Statement.TYPE_BASICBLOCK); + stat_type.put("sequence", Statement.TYPE_SEQUENCE); + + expr_type.put("array", Exprent.EXPRENT_ARRAY); + expr_type.put("assignment", Exprent.EXPRENT_ASSIGNMENT); + expr_type.put("constant", Exprent.EXPRENT_CONST); + expr_type.put("exit", Exprent.EXPRENT_EXIT); + expr_type.put("field", Exprent.EXPRENT_FIELD); + expr_type.put("function", Exprent.EXPRENT_FUNCTION); + expr_type.put("if", Exprent.EXPRENT_IF); + expr_type.put("invocation", Exprent.EXPRENT_INVOCATION); + expr_type.put("monitor", Exprent.EXPRENT_MONITOR); + expr_type.put("new", Exprent.EXPRENT_NEW); + expr_type.put("switch", Exprent.EXPRENT_SWITCH); + expr_type.put("var", Exprent.EXPRENT_VAR); + expr_type.put("annotation", Exprent.EXPRENT_ANNOTATION); + expr_type.put("assert", Exprent.EXPRENT_ASSERT); + + expr_func_type.put("eq", FunctionExprent.FUNCTION_EQ); + + expr_exit_type.put("return", ExitExprent.EXIT_RETURN); + expr_exit_type.put("throw", ExitExprent.EXIT_THROW); + + stat_if_type.put("if", IfStatement.IFTYPE_IF); + stat_if_type.put("ifelse", IfStatement.IFTYPE_IFELSE); + + expr_const_type.put("null", VarType.VARTYPE_NULL); + expr_const_type.put("string", VarType.VARTYPE_STRING); + } + + private final MatchNode rootNode; + private final Map variables = new HashMap<>(); + + public MatchEngine(String description) { + // each line is a separate statement/exprent + String[] lines = description.split("\n"); + + int depth = 0; + LinkedList stack = new LinkedList<>(); + + for (String line : lines) { + List properties = new ArrayList<>(Arrays.asList(line.split("\\s+"))); // split on any number of whitespaces + if (properties.get(0).isEmpty()) { + properties.remove(0); + } + + int node_type = "statement".equals(properties.get(0)) ? MatchNode.MATCHNODE_STATEMENT : MatchNode.MATCHNODE_EXPRENT; + + // create new node + MatchNode matchNode = new MatchNode(node_type); + for (int i = 1; i < properties.size(); ++i) { + String[] values = properties.get(i).split(":"); + + MatchProperties property = (node_type == MatchNode.MATCHNODE_STATEMENT ? stat_properties : expr_properties).get(values[0]); + if (property == null) { // unknown property defined + throw new RuntimeException("Unknown matching property"); + } + else { + Object value; + int parameter = 0; + + String strValue = values[1]; + if (values.length == 3) { + parameter = Integer.parseInt(values[1]); + strValue = values[2]; + } + + switch (property) { + case STATEMENT_TYPE: + value = stat_type.get(strValue); + break; + case STATEMENT_STATSIZE: + case STATEMENT_EXPRSIZE: + value = Integer.valueOf(strValue); + break; + case STATEMENT_POSITION: + case EXPRENT_POSITION: + case EXPRENT_INVOCATION_CLASS: + case EXPRENT_INVOCATION_SIGNATURE: + case EXPRENT_INVOCATION_PARAMETER: + case EXPRENT_VAR_INDEX: + case EXPRENT_FIELD_NAME: + case EXPRENT_CONSTVALUE: + case STATEMENT_RET: + case EXPRENT_RET: + value = strValue; + break; + case STATEMENT_IFTYPE: + value = stat_if_type.get(strValue); + break; + case EXPRENT_FUNCTYPE: + value = expr_func_type.get(strValue); + break; + case EXPRENT_EXITTYPE: + value = expr_exit_type.get(strValue); + break; + case EXPRENT_CONSTTYPE: + value = expr_const_type.get(strValue); + break; + case EXPRENT_TYPE: + value = expr_type.get(strValue); + break; + default: + throw new RuntimeException("Unhandled matching property"); + } + + matchNode.addRule(property, new RuleValue(parameter, value)); + } + } + + if (stack.isEmpty()) { // first line, root node + stack.push(matchNode); + } + else { + // return to the correct parent on the stack + int new_depth = line.lastIndexOf(' ', depth) + 1; + for (int i = new_depth; i <= depth; ++i) { + stack.pop(); + } + + // insert new node + stack.getFirst().addChild(matchNode); + stack.push(matchNode); + + depth = new_depth; + } + } + + this.rootNode = stack.getLast(); + } + + public boolean match(IMatchable object) { + variables.clear(); + return match(this.rootNode, object); + } + + private boolean match(MatchNode matchNode, IMatchable object) { + if (!object.match(matchNode, this)) { + return false; + } + + int expr_index = 0; + int stat_index = 0; + for (MatchNode childNode : matchNode.getChildren()) { + boolean isStatement = childNode.getType() == MatchNode.MATCHNODE_STATEMENT; + + IMatchable childObject = object.findObject(childNode, isStatement ? stat_index : expr_index); + if (childObject == null || !match(childNode, childObject)) { + return false; + } + + if (isStatement) { + stat_index++; + } + else { + expr_index++; + } + } + + return true; + } + + public boolean checkAndSetVariableValue(String name, Object value) { + Object old_value = variables.get(name); + if (old_value != null) { + return old_value.equals(value); + } + else { + variables.put(name, value); + return true; + } + } + + public Object getVariableValue(String name) { + return variables.get(name); + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/match/MatchNode.java b/src/org/jetbrains/java/decompiler/struct/match/MatchNode.java new file mode 100644 index 0000000..ff33e75 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/match/MatchNode.java @@ -0,0 +1,66 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.struct.match; + +import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MatchNode { + public static class RuleValue { + public final int parameter; + public final Object value; + + public RuleValue(int parameter, Object value) { + this.parameter = parameter; + this.value = value; + } + + public boolean isVariable() { + String strValue = value.toString(); + return (strValue.charAt(0) == '$' && strValue.charAt(strValue.length() - 1) == '$'); + } + + public String toString() { + return value.toString(); + } + } + + public static final int MATCHNODE_STATEMENT = 0; + public static final int MATCHNODE_EXPRENT = 1; + + private final int type; + private final Map rules = new HashMap<>(); + private final List children = new ArrayList<>(); + + public MatchNode(int type) { + this.type = type; + } + + public void addChild(MatchNode child) { + children.add(child); + } + + public void addRule(MatchProperties property, RuleValue value) { + rules.put(property, value); + } + + public int getType() { + return type; + } + + public List getChildren() { + return children; + } + + public Map getRules() { + return rules; + } + + public Object getRuleValue(MatchProperties property) { + RuleValue rule = rules.get(property); + return rule == null ? null : rule.value; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/util/DataInputFullStream.java b/src/org/jetbrains/java/decompiler/util/DataInputFullStream.java index 011edde..286f315 100644 --- a/src/org/jetbrains/java/decompiler/util/DataInputFullStream.java +++ b/src/org/jetbrains/java/decompiler/util/DataInputFullStream.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.util; import java.io.ByteArrayInputStream; @@ -20,36 +6,15 @@ import java.io.DataInputStream; import java.io.IOException; public class DataInputFullStream extends DataInputStream { - public DataInputFullStream(byte[] bytes) { super(new ByteArrayInputStream(bytes)); } - public int readFull(byte[] b) throws IOException { - int length = b.length; - byte[] temp = new byte[length]; - int pos = 0; - - int bytes_read; - while (true) { - bytes_read = read(temp, 0, length - pos); - if (bytes_read == -1) { - return -1; - } - - System.arraycopy(temp, 0, b, pos, bytes_read); - pos += bytes_read; - if (pos == length) { - break; - } - } - - return length; + public byte[] read(int n) throws IOException { + return InterpreterUtil.readBytes(this, n); } public void discard(int n) throws IOException { - if (super.skip(n) != n) { - throw new IOException("Skip failed"); - } + InterpreterUtil.discardBytes(this, n); } -} +} \ 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 a26b77e..265a538 100644 --- a/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java +++ b/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java @@ -1,27 +1,13 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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.*; public class FastFixedSetFactory { - private VBStyleCollection colValuesInternal = new VBStyleCollection(); + private final VBStyleCollection colValuesInternal = new VBStyleCollection<>(); - private int dataLength; + private final int dataLength; public FastFixedSetFactory(Collection set) { @@ -46,7 +32,7 @@ public class FastFixedSetFactory { } public FastFixedSet spawnEmptySet() { - return new FastFixedSet(this); + return new FastFixedSet<>(this); } private int getDataLength() { @@ -57,11 +43,11 @@ public class FastFixedSetFactory { return colValuesInternal; } - public static class FastFixedSet implements Iterable { + public static final class FastFixedSet implements Iterable { - private FastFixedSetFactory factory; + private final FastFixedSetFactory factory; - private VBStyleCollection colValuesInternal; + private final VBStyleCollection colValuesInternal; private int[] data; @@ -74,11 +60,10 @@ public class FastFixedSetFactory { public FastFixedSet getCopy() { - FastFixedSet copy = new FastFixedSet(factory); + 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; @@ -111,12 +96,6 @@ public class FastFixedSetFactory { data[index[0]] &= ~index[1]; } - public void removeAll(Collection set) { - for (E element : set) { - remove(element); - } - } - public boolean contains(E element) { int[] index = colValuesInternal.getWithKey(element); return (data[index[0]] & index[1]) != 0; @@ -153,15 +132,6 @@ public class FastFixedSetFactory { } } - public void symdiff(FastFixedSet set) { - int[] extdata = set.getData(); - int[] intdata = data; - - for (int i = intdata.length - 1; i >= 0; i--) { - intdata[i] ^= extdata[i]; - } - } - public void complement(FastFixedSet set) { int[] extdata = set.getData(); int[] intdata = data; @@ -174,7 +144,7 @@ public class FastFixedSetFactory { public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof FastFixedSet)) return false; + if (!(o instanceof FastFixedSet)) return false; int[] extdata = ((FastFixedSet)o).getData(); int[] intdata = data; @@ -200,19 +170,15 @@ public class FastFixedSetFactory { return true; } + @Override public Iterator iterator() { - return new FastFixedSetIterator(this); + return new FastFixedSetIterator<>(this); } public Set toPlainSet() { - return toPlainCollection(new HashSet()); + return toPlainCollection(new HashSet<>()); } - public List toPlainList() { - return toPlainCollection(new ArrayList()); - } - - private > T toPlainCollection(T cl) { int[] intdata = data; @@ -233,18 +199,6 @@ public class FastFixedSetFactory { return cl; } - public String toBinary() { - - StringBuilder buffer = new StringBuilder(); - int[] intdata = data; - - for (int i = 0; i < intdata.length; i++) { - buffer.append(" ").append(Integer.toBinaryString(intdata[i])); - } - - return buffer.toString(); - } - public String toString() { StringBuilder buffer = new StringBuilder("{"); @@ -284,11 +238,11 @@ public class FastFixedSetFactory { } } - public static class FastFixedSetIterator implements Iterator { + public static final class FastFixedSetIterator implements Iterator { - private VBStyleCollection colValuesInternal; - private int[] data; - private int size; + private final VBStyleCollection colValuesInternal; + private final int[] data; + private final int size; private int pointer = -1; private int next_pointer = -1; @@ -331,11 +285,13 @@ public class FastFixedSetFactory { return -1; } + @Override public boolean hasNext() { next_pointer = getNextIndex(pointer); return (next_pointer >= 0); } + @Override public E next() { if (next_pointer >= 0) { pointer = next_pointer; @@ -351,10 +307,10 @@ public class FastFixedSetFactory { return pointer < size ? colValuesInternal.getKey(pointer) : null; } + @Override public void remove() { int[] index = colValuesInternal.get(pointer); data[index[0]] &= ~index[1]; } } -} - +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/util/FastSetFactory.java b/src/org/jetbrains/java/decompiler/util/FastSetFactory.java deleted file mode 100644 index e9cce03..0000000 --- a/src/org/jetbrains/java/decompiler/util/FastSetFactory.java +++ /dev/null @@ -1,489 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.util; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -public class FastSetFactory { - - private VBStyleCollection colValuesInternal = new VBStyleCollection(); - - private int lastBlock; - - private int lastMask; - - public FastSetFactory(Collection set) { - - int block = -1; - int mask = -1; - int index = 0; - - for (E element : set) { - - block = index / 32; - - if (index % 32 == 0) { - mask = 1; - } - else { - mask <<= 1; - } - - colValuesInternal.putWithKey(new int[]{block, mask}, element); - - index++; - } - - lastBlock = block; - lastMask = mask; - } - - private int[] addElement(E element) { - - if (lastMask == -1 || lastMask == 0x80000000) { - lastMask = 1; - lastBlock++; - } - else { - lastMask <<= 1; - } - - int[] pointer = new int[]{lastBlock, lastMask}; - colValuesInternal.putWithKey(pointer, element); - - return pointer; - } - - public FastSet spawnEmptySet() { - return new FastSet(this); - } - - public int getLastBlock() { - return lastBlock; - } - - public int getLastMask() { - return lastMask; - } - - private VBStyleCollection getInternalValuesCollection() { - return colValuesInternal; - } - - - public static class FastSet implements Iterable { - - private FastSetFactory factory; - - private VBStyleCollection colValuesInternal; - - private int[] data; - - private FastSet(FastSetFactory factory) { - this.factory = factory; - this.colValuesInternal = factory.getInternalValuesCollection(); - this.data = new int[factory.getLastBlock() + 1]; - } - - public FastSet getCopy() { - - FastSet copy = new FastSet(factory); - - int arrlength = data.length; - int[] cpdata = new int[arrlength]; - - System.arraycopy(data, 0, cpdata, 0, arrlength); - copy.setData(cpdata); - - return copy; - } - - private int[] ensureCapacity(int index) { - - int newlength = data.length; - if (newlength == 0) { - newlength = 1; - } - - while (newlength <= index) { - newlength *= 2; - } - - int[] newdata = new int[newlength]; - System.arraycopy(data, 0, newdata, 0, data.length); - - return data = newdata; - } - - public void add(E element) { - int[] index = colValuesInternal.getWithKey(element); - - if (index == null) { - index = factory.addElement(element); - } - - if (index[0] >= data.length) { - ensureCapacity(index[0]); - } - - data[index[0]] |= index[1]; - } - - public void setAllElements() { - - int lastblock = factory.getLastBlock(); - int lastmask = factory.getLastMask(); - - if (lastblock >= data.length) { - ensureCapacity(lastblock); - } - - for (int i = lastblock - 1; i >= 0; i--) { - data[i] = 0xFFFFFFFF; - } - - data[lastblock] = lastmask | (lastmask - 1); - } - - public void addAll(Set set) { - for (E element : set) { - add(element); - } - } - - public void remove(E element) { - int[] index = colValuesInternal.getWithKey(element); - - if (index == null) { - index = factory.addElement(element); - } - - if (index[0] < data.length) { - data[index[0]] &= ~index[1]; - } - } - - public void removeAll(Set set) { - for (E element : set) { - remove(element); - } - } - - public boolean contains(E element) { - int[] index = colValuesInternal.getWithKey(element); - - if (index == null) { - index = factory.addElement(element); - } - - return index[0] >= data.length ? false : ((data[index[0]] & index[1]) != 0); - } - - public boolean contains(FastSet set) { - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for (int i = minlength - 1; i >= 0; i--) { - if ((extdata[i] & ~intdata[i]) != 0) { - return false; - } - } - - for (int i = extdata.length - 1; i >= minlength; i--) { - if (extdata[i] != 0) { - return false; - } - } - - return true; - } - - public void union(FastSet set) { - - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for (int i = minlength - 1; i >= 0; i--) { - intdata[i] |= extdata[i]; - } - - boolean expanded = false; - for (int i = extdata.length - 1; i >= minlength; i--) { - if (extdata[i] != 0) { - if (!expanded) { - intdata = ensureCapacity(extdata.length - 1); - } - intdata[i] = extdata[i]; - } - } - } - - public void intersection(FastSet set) { - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for (int i = minlength - 1; i >= 0; i--) { - intdata[i] &= extdata[i]; - } - - for (int i = intdata.length - 1; i >= minlength; i--) { - intdata[i] = 0; - } - } - - public void symdiff(FastSet set) { - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for (int i = minlength - 1; i >= 0; i--) { - intdata[i] ^= extdata[i]; - } - - boolean expanded = false; - for (int i = extdata.length - 1; i >= minlength; i--) { - if (extdata[i] != 0) { - if (!expanded) { - intdata = ensureCapacity(extdata.length - 1); - } - intdata[i] = extdata[i]; - } - } - } - - public void complement(FastSet set) { - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for (int i = minlength - 1; i >= 0; i--) { - intdata[i] &= ~extdata[i]; - } - } - - - public boolean equals(Object o) { - if (o == this) return true; - if (o == null || !(o instanceof FastSet)) return false; - - int[] longdata = ((FastSet)o).getData(); - int[] shortdata = data; - - if (data.length > longdata.length) { - shortdata = longdata; - longdata = data; - } - - for (int i = shortdata.length - 1; i >= 0; i--) { - if (shortdata[i] != longdata[i]) { - return false; - } - } - - for (int i = longdata.length - 1; i >= shortdata.length; i--) { - if (longdata[i] != 0) { - return false; - } - } - - return true; - } - - public int getCardinality() { - - boolean found = false; - int[] intdata = data; - - for (int i = intdata.length - 1; i >= 0; i--) { - int block = intdata[i]; - if (block != 0) { - if (found) { - return 2; - } - else { - if ((block & (block - 1)) == 0) { - found = true; - } - else { - return 2; - } - } - } - } - - return found ? 1 : 0; - } - - public int size() { - - int size = 0; - int[] intdata = data; - - for (int i = intdata.length - 1; i >= 0; i--) { - size += Integer.bitCount(intdata[i]); - } - - return size; - } - - public boolean isEmpty() { - int[] intdata = data; - - for (int i = intdata.length - 1; i >= 0; i--) { - if (intdata[i] != 0) { - return false; - } - } - - return true; - } - - public Iterator iterator() { - return new FastSetIterator(this); - } - - public Set toPlainSet() { - HashSet set = new HashSet(); - - int[] intdata = data; - - int size = data.length * 32; - if (size > colValuesInternal.size()) { - size = colValuesInternal.size(); - } - - for (int i = size - 1; i >= 0; i--) { - int[] index = colValuesInternal.get(i); - - if ((intdata[index[0]] & index[1]) != 0) { - set.add(colValuesInternal.getKey(i)); - } - } - - return set; - } - - public String toBinary() { - - StringBuilder buffer = new StringBuilder(); - int[] intdata = data; - - for (int i = 0; i < intdata.length; i++) { - buffer.append(" ").append(Integer.toBinaryString(intdata[i])); - } - - return buffer.toString(); - } - - private int[] getData() { - return data; - } - - private void setData(int[] data) { - this.data = data; - } - - public int[] getLoad() { - int[] intdata = data; - int notempty = 0; - - for (int i = 0; i < intdata.length; i++) { - if (intdata[i] != 0) { - notempty++; - } - } - - return new int[]{intdata.length, notempty}; - } - - public FastSetFactory getFactory() { - return factory; - } - } - - public static class FastSetIterator implements Iterator { - - private VBStyleCollection colValuesInternal; - private int[] data; - private int size; - - private int pointer = -1; - private int next_pointer = -1; - - private FastSetIterator(FastSet set) { - colValuesInternal = set.getFactory().getInternalValuesCollection(); - data = set.getData(); - - size = colValuesInternal.size(); - int datasize = data.length * 32; - - if (datasize < size) { - size = datasize; - } - } - - public boolean hasNext() { - - next_pointer = pointer; - - while (++next_pointer < size) { - int[] index = colValuesInternal.get(next_pointer); - if ((data[index[0]] & index[1]) != 0) { - return true; - } - } - - next_pointer = -1; - return false; - } - - public E next() { - if (next_pointer >= 0) { - pointer = next_pointer; - } - else { - while (++pointer < size) { - int[] index = colValuesInternal.get(pointer); - if ((data[index[0]] & index[1]) != 0) { - break; - } - } - } - - next_pointer = -1; - return pointer < size ? colValuesInternal.getKey(pointer) : null; - } - - public void remove() { - int[] index = colValuesInternal.get(pointer); - data[index[0]] &= ~index[1]; - - pointer--; - } - } -} - diff --git a/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java b/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java index 64e98a0..9b66a2e 100644 --- a/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java +++ b/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java @@ -1,34 +1,17 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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 { - private VBStyleCollection colValuesInternal = new VBStyleCollection(); + private final VBStyleCollection colValuesInternal = new VBStyleCollection<>(); private int lastBlock; private int lastMask; - public FastSparseSetFactory(Collection set) { + public FastSparseSetFactory(Collection set) { int block = -1; int mask = -1; @@ -71,28 +54,24 @@ public class FastSparseSetFactory { } public FastSparseSet spawnEmptySet() { - return new FastSparseSet(this); + return new FastSparseSet<>(this); } public int getLastBlock() { return lastBlock; } - public int getLastMask() { - return lastMask; - } - private VBStyleCollection getInternalValuesCollection() { return colValuesInternal; } - public static class FastSparseSet implements Iterable { + public static final class FastSparseSet implements Iterable { public static final FastSparseSet[] EMPTY_ARRAY = new FastSparseSet[0]; - private FastSparseSetFactory factory; + private final FastSparseSetFactory factory; - private VBStyleCollection colValuesInternal; + private final VBStyleCollection colValuesInternal; private int[] data; private int[] next; @@ -115,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) { @@ -137,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) { @@ -165,30 +131,6 @@ public class FastSparseSetFactory { changeNext(next, block, next[block], block); } - public void setAllElements() { - - int lastblock = factory.getLastBlock(); - int lastmask = factory.getLastMask(); - - if (lastblock >= data.length) { - ensureCapacity(lastblock); - } - - for (int i = lastblock - 1; i >= 0; i--) { - data[i] = 0xFFFFFFFF; - next[i] = i + 1; - } - - data[lastblock] = lastmask | (lastmask - 1); - next[lastblock] = 0; - } - - public void addAll(Set set) { - for (E element : set) { - add(element); - } - } - public void remove(E element) { int[] index = colValuesInternal.getWithKey(element); @@ -206,12 +148,6 @@ public class FastSparseSetFactory { } } - public void removeAll(Set set) { - for (E element : set) { - remove(element); - } - } - public boolean contains(E element) { int[] index = colValuesInternal.getWithKey(element); @@ -219,28 +155,7 @@ public class FastSparseSetFactory { index = factory.addElement(element); } - return index[0] >= data.length ? false : ((data[index[0]] & index[1]) != 0); - } - - public boolean contains(FastSparseSet set) { - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for (int i = minlength - 1; i >= 0; i--) { - if ((extdata[i] & ~intdata[i]) != 0) { - return false; - } - } - - for (int i = extdata.length - 1; i >= minlength; i--) { - if (extdata[i] != 0) { - return false; - } - } - - return true; + return index[0] < data.length && ((data[index[0]] & index[1]) != 0); } private void setNext() { @@ -307,29 +222,6 @@ public class FastSparseSetFactory { setNext(); } - public void symdiff(FastSparseSet set) { - int[] extdata = set.getData(); - int[] intdata = data; - - int minlength = Math.min(extdata.length, intdata.length); - - for (int i = minlength - 1; i >= 0; i--) { - intdata[i] ^= extdata[i]; - } - - boolean expanded = false; - for (int i = extdata.length - 1; i >= minlength; i--) { - if (extdata[i] != 0) { - if (!expanded) { - intdata = ensureCapacity(extdata.length - 1); - } - intdata[i] = extdata[i]; - } - } - - setNext(); - } - public void complement(FastSparseSet set) { int[] extdata = set.getData(); @@ -355,7 +247,7 @@ public class FastSparseSetFactory { public boolean equals(Object o) { if (o == this) return true; - if (o == null || !(o instanceof FastSparseSet)) return false; + if (!(o instanceof FastSparseSet)) return false; int[] longdata = ((FastSparseSet)o).getData(); int[] shortdata = data; @@ -409,12 +301,13 @@ public class FastSparseSetFactory { return data.length == 0 || (next[0] == 0 && data[0] == 0); } + @Override public Iterator iterator() { - return new FastSparseSetIterator(this); + return new FastSparseSetIterator<>(this); } public Set toPlainSet() { - HashSet set = new HashSet(); + HashSet set = new HashSet<>(); int[] intdata = data; @@ -438,18 +331,6 @@ public class FastSparseSetFactory { return toPlainSet().toString(); } - public String toBinary() { - - StringBuilder buffer = new StringBuilder(); - int[] intdata = data; - - for (int i = 0; i < intdata.length; i++) { - buffer.append(" ").append(Integer.toBinaryString(intdata[i])); - } - - return buffer.toString(); - } - private int[] getData() { return data; } @@ -458,30 +339,17 @@ public class FastSparseSetFactory { return next; } - public int[] getLoad() { - int[] intdata = data; - int notempty = 0; - - for (int i = 0; i < intdata.length; i++) { - if (intdata[i] != 0) { - notempty++; - } - } - - return new int[]{intdata.length, notempty}; - } - public FastSparseSetFactory getFactory() { return factory; } } - public static class FastSparseSetIterator implements Iterator { + public static final class FastSparseSetIterator implements Iterator { - private VBStyleCollection colValuesInternal; - private int[] data; - private int[] next; - private int size; + private final VBStyleCollection colValuesInternal; + private final int[] data; + private final int[] next; + private final int size; private int pointer = -1; private int next_pointer = -1; @@ -524,11 +392,13 @@ public class FastSparseSetFactory { return -1; } + @Override public boolean hasNext() { next_pointer = getNextIndex(pointer); return (next_pointer >= 0); } + @Override public E next() { if (next_pointer >= 0) { pointer = next_pointer; @@ -544,6 +414,7 @@ public class FastSparseSetFactory { return pointer < size ? colValuesInternal.getKey(pointer) : null; } + @Override public void remove() { int[] index = colValuesInternal.get(pointer); data[index[0]] &= ~index[1]; diff --git a/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java b/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java index 67af3ad..0ba7dc4 100644 --- a/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java +++ b/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java @@ -1,58 +1,23 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.TextBuffer; -import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; - import java.io.*; -import java.nio.channels.FileChannel; import java.util.Collection; import java.util.HashSet; 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]; - private static final int CHANNEL_WINDOW_SIZE = IS_WINDOWS ? 64 * 1024 * 1024 - (32 * 1024) : 64 * 1024 * 1024; // magic number for Windows private static final int BUFFER_SIZE = 16 * 1024; - public static void copyFile(File in, File out) throws IOException { - FileInputStream inStream = new FileInputStream(in); - try { - FileOutputStream outStream = new FileOutputStream(out); - try { - FileChannel inChannel = inStream.getChannel(); - FileChannel outChannel = outStream.getChannel(); - long size = inChannel.size(), position = 0; - while (position < size) { - position += inChannel.transferTo(position, CHANNEL_WINDOW_SIZE, outChannel); - } - } - finally { - outStream.close(); - } - } - finally { - inStream.close(); + public static void copyFile(File source, File target) throws IOException { + try (FileInputStream in = new FileInputStream(source); FileOutputStream out = new FileOutputStream(target)) { + copyStream(in, out); } } @@ -65,39 +30,36 @@ public class InterpreterUtil { } public static byte[] getBytes(ZipFile archive, ZipEntry entry) throws IOException { - return readAndClose(archive.getInputStream(entry), (int)entry.getSize()); + try (InputStream stream = archive.getInputStream(entry)) { + return readBytes(stream, (int)entry.getSize()); + } } public static byte[] getBytes(File file) throws IOException { - return readAndClose(new FileInputStream(file), (int)file.length()); + try (FileInputStream stream = new FileInputStream(file)) { + return readBytes(stream, (int)file.length()); + } } - private static byte[] readAndClose(InputStream stream, int length) throws IOException { - try { - byte[] bytes = new byte[length]; - int n = 0, off = 0; - while (n < length) { - int count = stream.read(bytes, off + n, length - n); - if (count < 0) { - throw new IOException("premature end of stream"); - } - n += count; + public static byte[] readBytes(InputStream stream, int length) throws IOException { + byte[] bytes = new byte[length]; + + int n = 0, off = 0; + while (n < length) { + int count = stream.read(bytes, off + n, length - n); + if (count < 0) { + throw new IOException("premature end of stream"); } - return bytes; - } - finally { - stream.close(); + n += count; } + + return bytes; } - public static String getIndentString(int length) { - if (length == 0) return ""; - StringBuilder buf = new StringBuilder(); - String indent = (String)DecompilerContext.getProperty(IFernflowerPreferences.INDENT_STRING); - while (length-- > 0) { - buf.append(indent); + public static void discardBytes(InputStream stream, int length) throws IOException { + if (stream.skip(length) != length) { + throw new IOException("premature end of stream"); } - return buf.toString(); } public static boolean equalSets(Collection c1, Collection c2) { @@ -112,7 +74,7 @@ public class InterpreterUtil { return false; } - HashSet set = new HashSet(c1); + HashSet set = new HashSet<>(c1); set.removeAll(c2); return (set.size() == 0); } @@ -121,25 +83,6 @@ public class InterpreterUtil { return first == null ? second == null : first.equals(second); } - public static boolean equalObjectArrays(Object[] first, Object[] second) { - if (first == null || second == null) { - return equalObjects(first, second); - } - else { - if (first.length != second.length) { - return false; - } - - for (int i = 0; i < first.length; i++) { - if (!equalObjects(first[i], second[i])) { - return false; - } - } - } - - return true; - } - public static boolean equalLists(List first, List second) { if (first == null) { return second == null; @@ -160,19 +103,11 @@ public class InterpreterUtil { return false; } - public static boolean isPrintableUnicode(char c) { - int t = Character.getType(c); - return t != Character.UNASSIGNED && t != Character.LINE_SEPARATOR && t != Character.PARAGRAPH_SEPARATOR && - t != Character.CONTROL && t != Character.FORMAT && t != Character.PRIVATE_USE && t != Character.SURROGATE; - } - - public static String charToUnicodeLiteral(int value) { - String sTemp = Integer.toHexString(value); - sTemp = ("0000" + sTemp).substring(sTemp.length()); - return "\\u" + sTemp; - } - public static String makeUniqueKey(String name, String descriptor) { - return name + " " + descriptor; + return name + ' ' + descriptor; } -} + + public static String makeUniqueKey(String name, String descriptor1, String descriptor2) { + return name + ' ' + descriptor1 + ' ' + descriptor2; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/util/ListStack.java b/src/org/jetbrains/java/decompiler/util/ListStack.java index 19c2416..ac0dad9 100644 --- a/src/org/jetbrains/java/decompiler/util/ListStack.java +++ b/src/org/jetbrains/java/decompiler/util/ListStack.java @@ -1,44 +1,30 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.util; import java.util.ArrayList; public class ListStack extends ArrayList { - protected int pointer = 0; public ListStack() { super(); } - public ListStack(ArrayList list) { + public ListStack(ArrayList list) { super(list); } + @Override + @SuppressWarnings("MethodDoesntCallSuperMethod") public ListStack clone() { - ListStack newstack = new ListStack(this); - newstack.pointer = this.pointer; - return newstack; + ListStack copy = new ListStack<>(this); + copy.pointer = this.pointer; + return copy; } - public T push(T item) { + public void push(T item) { this.add(item); pointer++; - return item; } public T pop() { @@ -56,11 +42,6 @@ public class ListStack extends ArrayList { return o; } - public void remove() { - pointer--; - this.remove(pointer); - } - public void removeMultiple(int count) { while (count > 0) { pointer--; @@ -69,10 +50,6 @@ public class ListStack extends ArrayList { } } - public boolean empty() { - return (pointer == 0); - } - public int getPointer() { return pointer; } @@ -86,8 +63,9 @@ public class ListStack extends ArrayList { pointer++; } + @Override public void clear() { super.clear(); pointer = 0; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java b/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java index b70dede..865e1e6 100644 --- a/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java +++ b/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java @@ -1,24 +1,11 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.util; 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; @@ -27,9 +14,9 @@ public class SFormsFastMapDirect { private int size; - @SuppressWarnings("unchecked") private FastSparseSet[][] elements = new FastSparseSet[3][]; + @SuppressWarnings("unchecked") private final FastSparseSet[][] elements = new FastSparseSet[3][]; - private int[][] next = new int[3][]; + private final int[][] next = new int[3][]; public SFormsFastMapDirect() { this(true); @@ -80,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; @@ -119,10 +104,6 @@ public class SFormsFastMapDirect { putInternal(key, value, false); } - public void remove(int key) { - putInternal(key, null, true); - } - public void removeAllFields() { FastSparseSet[] arr = elements[2]; int[] arrnext = next[2]; @@ -353,7 +334,7 @@ public class SFormsFastMapDirect { } public List>> entryList() { - List>> list = new ArrayList>>(); + List>> list = new ArrayList<>(); for (int i = 2; i >= 0; i--) { int ikey = 0; @@ -361,22 +342,27 @@ 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 Integer var = key; - private FastSparseSet val = ent; + private final Integer var = key; + private final FastSparseSet val = ent; + @Override public Integer getKey() { return var; } + @Override public FastSparseSet getValue() { return val; } - public FastSparseSet setValue(FastSparseSet newvalue) { + @Override + public FastSparseSet setValue(FastSparseSet value) { return null; } + + }); } @@ -411,4 +397,4 @@ public class SFormsFastMapDirect { return arrnew; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/util/SortUtil.java b/src/org/jetbrains/java/decompiler/util/SortUtil.java deleted file mode 100644 index e227e02..0000000 --- a/src/org/jetbrains/java/decompiler/util/SortUtil.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.jetbrains.java.decompiler.util; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - -public class SortUtil -{ - public static interface Indexed - { - public int getSortIndex(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Iterator sortIndexed(Iterator itr) - { - List list = new ArrayList(); - List def_dec = new ArrayList(); - int first = -1; - - while(itr.hasNext()) - { - Object i = itr.next(); - //Split off any default variable declarations and sort them. - if (i instanceof Indexed && ((Indexed)i).getSortIndex() >= 0) - { - if (first == -1) first = list.size(); - def_dec.add((Indexed)i); - } - else - { - list.add(i); - } - } - - if (def_dec.size() > 0) - { - Collections.sort(def_dec, new Comparator() - { - @Override - public int compare(Indexed o1, Indexed o2) - { - return o1.getSortIndex() - o2.getSortIndex(); - } - }); - list.addAll(first, def_dec); - } - - return list.iterator(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Iterator sortComparable(Iterator itr) - { - List list = new ArrayList(); - - while (itr.hasNext()) - list.add(itr.next()); - - Collections.sort(list); - return list.iterator(); - } -} diff --git a/src/org/jetbrains/java/decompiler/util/TextBuffer.java b/src/org/jetbrains/java/decompiler/util/TextBuffer.java new file mode 100644 index 0000000..2d09c2d --- /dev/null +++ b/src/org/jetbrains/java/decompiler/util/TextBuffer.java @@ -0,0 +1,285 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ +package org.jetbrains.java.decompiler.util; + +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; + +import java.util.*; + +/** + * Allows to connect text with resulting lines + * + * @author egor + */ +@SuppressWarnings("UnusedReturnValue") +public class TextBuffer { + private final String myLineSeparator = DecompilerContext.getNewLineSeparator(); + private final String myIndent = (String)DecompilerContext.getProperty(IFernflowerPreferences.INDENT_STRING); + private final StringBuilder myStringBuilder; + private Map myLineToOffsetMapping = null; + + public TextBuffer() { + myStringBuilder = new StringBuilder(); + } + + public TextBuffer(int size) { + myStringBuilder = new StringBuilder(size); + } + + public TextBuffer(String text) { + myStringBuilder = new StringBuilder(text); + } + + public TextBuffer append(String str) { + myStringBuilder.append(str); + return this; + } + + public TextBuffer append(char ch) { + myStringBuilder.append(ch); + return this; + } + + public TextBuffer append(int i) { + myStringBuilder.append(i); + return this; + } + + public TextBuffer appendLineSeparator() { + myStringBuilder.append(myLineSeparator); + return this; + } + + public TextBuffer appendIndent(int length) { + while (length-- > 0) { + append(myIndent); + } + return this; + } + + public TextBuffer prepend(String s) { + myStringBuilder.insert(0, s); + shiftMapping(s.length()); + return this; + } + + public TextBuffer enclose(String left, String right) { + prepend(left); + append(right); + return this; + } + + public boolean containsOnlyWhitespaces() { + for (int i = 0; i < myStringBuilder.length(); i++) { + if (myStringBuilder.charAt(i) != ' ') { + return false; + } + } + return true; + } + + @Override + public String toString() { + String original = myStringBuilder.toString(); + if (myLineToOffsetMapping == null || myLineToOffsetMapping.isEmpty()) { + if (myLineMapping != null) { + return addOriginalLineNumbers(); + } + return original; + } + else { + StringBuilder res = new StringBuilder(); + String[] srcLines = original.split(myLineSeparator); + int currentLineStartOffset = 0; + int currentLine = 0; + int previousMarkLine = 0; + int dumpedLines = 0; + ArrayList linesWithMarks = new ArrayList<>(myLineToOffsetMapping.keySet()); + Collections.sort(linesWithMarks); + for (Integer markLine : linesWithMarks) { + Integer markOffset = myLineToOffsetMapping.get(markLine); + while (currentLine < srcLines.length) { + String line = srcLines[currentLine]; + int lineEnd = currentLineStartOffset + line.length() + myLineSeparator.length(); + if (markOffset <= lineEnd) { + int requiredLine = markLine - 1; + int linesToAdd = requiredLine - dumpedLines; + dumpedLines = requiredLine; + appendLines(res, srcLines, previousMarkLine, currentLine, linesToAdd); + previousMarkLine = currentLine; + break; + } + currentLineStartOffset = lineEnd; + currentLine++; + } + } + if (previousMarkLine < srcLines.length) { + appendLines(res, srcLines, previousMarkLine, srcLines.length, srcLines.length - previousMarkLine); + } + + return res.toString(); + } + } + + private String addOriginalLineNumbers() { + StringBuilder sb = new StringBuilder(); + int lineStart = 0, lineEnd; + int count = 0, length = myLineSeparator.length(); + while ((lineEnd = myStringBuilder.indexOf(myLineSeparator, lineStart)) > 0) { + ++count; + sb.append(myStringBuilder.substring(lineStart, lineEnd)); + Set integers = myLineMapping.get(count); + if (integers != null) { + sb.append("//"); + for (Integer integer : integers) { + sb.append(' ').append(integer); + } + } + sb.append(myLineSeparator); + lineStart = lineEnd + length; + } + if (lineStart < myStringBuilder.length()) { + sb.append(myStringBuilder.substring(lineStart)); + } + return sb.toString(); + } + + private void appendLines(StringBuilder res, String[] srcLines, int from, int to, int requiredLineNumber) { + if (to - from > requiredLineNumber) { + List strings = compactLines(Arrays.asList(srcLines).subList(from, to) ,requiredLineNumber); + int separatorsRequired = requiredLineNumber - 1; + for (String s : strings) { + res.append(s); + if (separatorsRequired-- > 0) { + res.append(myLineSeparator); + } + } + res.append(myLineSeparator); + } + else if (to - from <= requiredLineNumber) { + for (int i = from; i < to; i++) { + res.append(srcLines[i]).append(myLineSeparator); + } + for (int i = 0; i < requiredLineNumber - to + from; i++) { + res.append(myLineSeparator); + } + } + } + + public int length() { + return myStringBuilder.length(); + } + + public void setStart(int position) { + myStringBuilder.delete(0, position); + shiftMapping(-position); + } + + public void setLength(int position) { + myStringBuilder.setLength(position); + if (myLineToOffsetMapping != null) { + Map newMap = new HashMap<>(); + for (Map.Entry entry : myLineToOffsetMapping.entrySet()) { + if (entry.getValue() <= position) { + newMap.put(entry.getKey(), entry.getValue()); + } + } + myLineToOffsetMapping = newMap; + } + } + + public TextBuffer append(TextBuffer buffer) { + if (buffer.myLineToOffsetMapping != null && !buffer.myLineToOffsetMapping.isEmpty()) { + checkMapCreated(); + for (Map.Entry entry : buffer.myLineToOffsetMapping.entrySet()) { + myLineToOffsetMapping.put(entry.getKey(), entry.getValue() + myStringBuilder.length()); + } + } + myStringBuilder.append(buffer.myStringBuilder); + return this; + } + + private void shiftMapping(int shiftOffset) { + if (myLineToOffsetMapping != null) { + Map newMap = new HashMap<>(); + for (Map.Entry entry : myLineToOffsetMapping.entrySet()) { + int newValue = entry.getValue(); + if (newValue >= 0) { + newValue += shiftOffset; + } + if (newValue >= 0) { + newMap.put(entry.getKey(), newValue); + } + } + myLineToOffsetMapping = newMap; + } + } + + private void checkMapCreated() { + if (myLineToOffsetMapping == null) { + myLineToOffsetMapping = new HashMap<>(); + } + } + + public int countLines() { + return countLines(0); + } + + public int countLines(int from) { + return count(myLineSeparator, from); + } + + public int count(String substring, int from) { + int count = 0, length = substring.length(), p = from; + while ((p = myStringBuilder.indexOf(substring, p)) > 0) { + ++count; + p += length; + } + return count; + } + + private static List compactLines(List srcLines, int requiredLineNumber) { + if (srcLines.size() < 2 || srcLines.size() <= requiredLineNumber) { + return srcLines; + } + List res = new LinkedList<>(srcLines); + // first join lines with a single { or } + for (int i = res.size()-1; i > 0 ; i--) { + String s = res.get(i); + if (s.trim().equals("{") || s.trim().equals("}")) { + res.set(i-1, res.get(i-1).concat(s)); + res.remove(i); + } + if (res.size() <= requiredLineNumber) { + return res; + } + } + // now join empty lines + for (int i = res.size()-1; i > 0 ; i--) { + String s = res.get(i); + if (s.trim().isEmpty()) { + res.set(i-1, res.get(i-1).concat(s)); + res.remove(i); + } + if (res.size() <= requiredLineNumber) { + return res; + } + } + return res; + } + + private Map> myLineMapping = null; // new to original + + public void dumpOriginalLineNumbers(int[] lineMapping) { + if (lineMapping.length > 0) { + myLineMapping = new HashMap<>(); + for (int i = 0; i < lineMapping.length; i += 2) { + int key = lineMapping[i + 1]; + Set existing = myLineMapping.computeIfAbsent(key, k -> new TreeSet<>()); + existing.add(lineMapping[i]); + } + } + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/util/TextUtil.java b/src/org/jetbrains/java/decompiler/util/TextUtil.java new file mode 100644 index 0000000..e2a08f0 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/util/TextUtil.java @@ -0,0 +1,282 @@ +// 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; +import org.jetbrains.java.decompiler.main.ClassesProcessor; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; + +import java.util.Arrays; +import java.util.HashSet; + +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", + "interface", "static", "void", "char", "finally", "long", "strictfp", "volatile", "class", "float", "native", "super", "while", + "const", "for", "new", "switch", "continue", "goto", "package", "synchronized", "true", "false", "null", "assert")); + + public static void writeQualifiedSuper(TextBuffer buf, String qualifier) { + ClassesProcessor.ClassNode classNode = (ClassesProcessor.ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); + if (!qualifier.equals(classNode.classStruct.qualifiedName)) { + buf.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(qualifier))).append('.'); + } + buf.append("super"); + } + + public static String getIndentString(int length) { + if (length == 0) return ""; + StringBuilder buf = new StringBuilder(); + String indent = (String)DecompilerContext.getProperty(IFernflowerPreferences.INDENT_STRING); + append(buf, indent, length); + return buf.toString(); + } + + public static void append(StringBuilder buf, String string, int times) { + while (times-- > 0) buf.append(string); + } + + public static boolean isPrintableUnicode(char c) { + int t = Character.getType(c); + return t != Character.UNASSIGNED && t != Character.LINE_SEPARATOR && t != Character.PARAGRAPH_SEPARATOR && + t != Character.CONTROL && t != Character.FORMAT && t != Character.PRIVATE_USE && t != Character.SURROGATE; + } + + public static String charToUnicodeLiteral(int value) { + String sTemp = Integer.toHexString(value); + sTemp = ("0000" + sTemp).substring(sTemp.length()); + return "\\u" + sTemp; + } + + public static boolean isValidIdentifier(String id, int version) { + return isJavaIdentifier(id) && !isKeyword(id, version); + } + + private static boolean isJavaIdentifier(String id) { + if (id.isEmpty() || !Character.isJavaIdentifierStart(id.charAt(0))) { + return false; + } + + for (int i = 1; i < id.length(); i++) { + if (!Character.isJavaIdentifierPart(id.charAt(i))) { + return false; + } + } + + return true; + } + + private static boolean isKeyword(String id, int version) { + return KEYWORDS.contains(id) || version >= CodeConstants.BYTECODE_JAVA_5 && "enum".equals(id); + } + + public static String getInstructionName(int opcode) { + return opcodeNames[opcode]; + } + + private static final String[] opcodeNames = { + "nop", + "aconst_null", + "iconst_m1", + "iconst_0", + "iconst_1", + "iconst_2", + "iconst_3", + "iconst_4", + "iconst_5", + "lconst_0", + "lconst_1", + "fconst_0", + "fconst_1", + "fconst_2", + "dconst_0", + "dconst_1", + "bipush", + "sipush", + "ldc", + "ldc_w", + "ldc2_w", + "iload", + "lload", + "fload", + "dload", + "aload", + "iload_0", + "iload_1", + "iload_2", + "iload_3", + "lload_0", + "lload_1", + "lload_2", + "lload_3", + "fload_0", + "fload_1", + "fload_2", + "fload_3", + "dload_0", + "dload_1", + "dload_2", + "dload_3", + "aload_0", + "aload_1", + "aload_2", + "aload_3", + "iaload", + "laload", + "faload", + "daload", + "aaload", + "baload", + "caload", + "saload", + "istore", + "lstore", + "fstore", + "dstore", + "astore", + "istore_0", + "istore_1", + "istore_2", + "istore_3", + "lstore_0", + "lstore_1", + "lstore_2", + "lstore_3", + "fstore_0", + "fstore_1", + "fstore_2", + "fstore_3", + "dstore_0", + "dstore_1", + "dstore_2", + "dstore_3", + "astore_0", + "astore_1", + "astore_2", + "astore_3", + "iastore", + "lastore", + "fastore", + "dastore", + "aastore", + "bastore", + "castore", + "sastore", + "pop", + "pop2", + "dup", + "dup_x1", + "dup_x2", + "dup2", + "dup2_x1", + "dup2_x2", + "swap", + "iadd", + "ladd", + "fadd", + "dadd", + "isub", + "lsub", + "fsub", + "dsub", + "imul", + "lmul", + "fmul", + "dmul", + "idiv", + "ldiv", + "fdiv", + "ddiv", + "irem", + "lrem", + "frem", + "drem", + "ineg", + "lneg", + "fneg", + "dneg", + "ishl", + "lshl", + "ishr", + "lshr", + "iushr", + "lushr", + "iand", + "land", + "ior", + "lor", + "ixor", + "lxor", + "iinc", + "i2l", + "i2f", + "i2d", + "l2i", + "l2f", + "l2d", + "f2i", + "f2l", + "f2d", + "d2i", + "d2l", + "d2f", + "i2b", + "i2c", + "i2s", + "lcmp", + "fcmpl", + "fcmpg", + "dcmpl", + "dcmpg", + "ifeq", + "ifne", + "iflt", + "ifge", + "ifgt", + "ifle", + "if_icmpeq", + "if_icmpne", + "if_icmplt", + "if_icmpge", + "if_icmpgt", + "if_icmple", + "if_acmpeq", + "if_acmpne", + "goto", + "jsr", + "ret", + "tableswitch", + "lookupswitch", + "ireturn", + "lreturn", + "freturn", + "dreturn", + "areturn", + "return", + "getstatic", + "putstatic", + "getfield", + "putfield", + "invokevirtual", + "invokespecial", + "invokestatic", + "invokeinterface", + "invokedynamic", // since Java 7 + "new", + "newarray", + "anewarray", + "arraylength", + "athrow", + "checkcast", + "instanceof", + "monitorenter", + "monitorexit", + "wide", + "multianewarray", + "ifnull", + "ifnonnull", + "goto_w", + "jsr_w" + }; +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/util/Util.java b/src/org/jetbrains/java/decompiler/util/Util.java deleted file mode 100644 index 5ade232..0000000 --- a/src/org/jetbrains/java/decompiler/util/Util.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.jetbrains.java.decompiler.util; - -import java.util.regex.Pattern; - -public class Util { - - private final static Pattern RTRIM = Pattern.compile("\\s+$"); - - public static String rtrim(String s) { - return RTRIM.matcher(s).replaceAll(""); - } -} diff --git a/src/org/jetbrains/java/decompiler/util/VBStyleCollection.java b/src/org/jetbrains/java/decompiler/util/VBStyleCollection.java index cdc1762..4454337 100644 --- a/src/org/jetbrains/java/decompiler/util/VBStyleCollection.java +++ b/src/org/jetbrains/java/decompiler/util/VBStyleCollection.java @@ -1,30 +1,15 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.util; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; public class VBStyleCollection extends ArrayList { - private HashMap map = new HashMap(); + private HashMap map = new HashMap<>(); - private ArrayList lstKeys = new ArrayList(); + private ArrayList lstKeys = new ArrayList<>(); public VBStyleCollection() { super(); @@ -32,24 +17,23 @@ public class VBStyleCollection extends ArrayList { public VBStyleCollection(int initialCapacity) { super(initialCapacity); - lstKeys = new ArrayList(initialCapacity); - map = new HashMap(initialCapacity); - } - - public VBStyleCollection(Collection c) { - super(c); + lstKeys = new ArrayList<>(initialCapacity); + map = new HashMap<>(initialCapacity); } + @Override public boolean add(E element) { lstKeys.add(null); super.add(element); return true; } + @Override public boolean remove(Object element) { // TODO: error on void remove(E element) throw new RuntimeException("not implemented!"); } + @Override public boolean addAll(Collection c) { for (int i = c.size() - 1; i >= 0; i--) { lstKeys.add(null); @@ -57,12 +41,6 @@ public class VBStyleCollection extends ArrayList { return super.addAll(c); } - public void addAllWithKey(VBStyleCollection c) { - for (int i = 0; i < c.size(); i++) { - addWithKey(c.get(i), c.getKey(i)); - } - } - public void addAllWithKey(Collection elements, Collection keys) { int index = super.size(); @@ -92,6 +70,7 @@ public class VBStyleCollection extends ArrayList { return null; } + @Override public void add(int index, E element) { addToListIndex(index, 1); lstKeys.add(index, null); @@ -100,19 +79,20 @@ public class VBStyleCollection extends ArrayList { public void addWithKeyAndIndex(int index, E element, K key) { addToListIndex(index, 1); - map.put(key, new Integer(index)); + map.put(key, index); super.add(index, element); lstKeys.add(index, key); } public void removeWithKey(K key) { - int index = map.get(key).intValue(); + int index = map.get(key); addToListIndex(index + 1, -1); super.remove(index); lstKeys.remove(index); map.remove(key); } + @Override public E remove(int index) { addToListIndex(index + 1, -1); K obj = lstKeys.get(index); @@ -128,11 +108,11 @@ public class VBStyleCollection extends ArrayList { if (index == null) { return null; } - return super.get(index.intValue()); + return super.get(index); } public int getIndexByKey(K key) { - return map.get(key).intValue(); + return map.get(key); } public E getLast() { @@ -143,40 +123,22 @@ public class VBStyleCollection extends ArrayList { return map.containsKey(key); } + @Override public void clear() { map.clear(); lstKeys.clear(); super.clear(); } + @Override public VBStyleCollection clone() { - VBStyleCollection c = new VBStyleCollection(); - c.addAll(new ArrayList(this)); - c.setMap(new HashMap(map)); - c.setLstKeys(new ArrayList(lstKeys)); + VBStyleCollection c = new VBStyleCollection<>(); + c.addAll(new ArrayList<>(this)); + c.setMap(new HashMap<>(map)); + c.setLstKeys(new ArrayList<>(lstKeys)); return c; } - public void swap(int index1, int index2) { - - Collections.swap(this, index1, index2); - Collections.swap(lstKeys, index1, index2); - - K key = lstKeys.get(index1); - if (key != null) { - map.put(key, new Integer(index1)); - } - - key = lstKeys.get(index2); - if (key != null) { - map.put(key, new Integer(index2)); - } - } - - public HashMap getMap() { - return map; - } - public void setMap(HashMap map) { this.map = map; } @@ -197,8 +159,8 @@ public class VBStyleCollection extends ArrayList { for (int i = lstKeys.size() - 1; i >= index; i--) { K obj = lstKeys.get(i); if (obj != null) { - map.put(obj, new Integer(i + diff)); + map.put(obj, i + diff); } } } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/util/VarHelper.java b/src/org/jetbrains/java/decompiler/util/VarHelper.java deleted file mode 100644 index 34b52a9..0000000 --- a/src/org/jetbrains/java/decompiler/util/VarHelper.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.jetbrains.java.decompiler.util; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -public class VarHelper { - - private static final Map switches = new HashMap(); - - static { - switches.put("byte", new String[]{ - "b" - }); - switches.put("char", new String[]{ - "c" - }); - switches.put("short", new String[]{ - "short" - }); - switches.put("int", new String[]{ - "i", "j", "k", "l" - }); - switches.put("long", new String[]{ - "i", "j", "k", "l" - }); - switches.put("boolean", new String[]{ - "flag" - }); - switches.put("double", new String[]{ - "d" - }); - switches.put("float", new String[]{ - "f", "f" // Add twice because the original script is inconsistent - }); - switches.put("String", new String[]{ - "s", "s" // Add twice because the original script is inconsistent - }); - switches.put("Class", new String[]{ - "oclass" - }); - switches.put("Long", new String[]{ - "olong" - }); - switches.put("Byte", new String[]{ - "obyte" - }); - switches.put("Short", new String[]{ - "oshort" - }); - switches.put("Boolean", new String[]{ - "obool" - }); - switches.put("Double", new String[]{ - "odouble" - }); - switches.put("Float", new String[]{ - "ofloat" - }); - switches.put("Long", new String[]{ - "olong" - }); - switches.put("Enum", new String[]{ - "oenum" - }); - } - private final Set used = new HashSet(); - - public String help(String name, String type, boolean varArgs) { - if (type == null || !name.startsWith("var")) { - return name; - } - - while (type.contains( "<" )) { - type = type.substring(0, type.indexOf('<')) + type.substring(type.lastIndexOf('>') + 1); - } - type = type.replace( '.', '_' ); - - if (type.endsWith("]")) { - type = "a" + type.substring(0, type.indexOf('[')); - } else if (varArgs) { - type = "a" + type; - } - - String[] remap = switches.get(type); - if (remap == null) { - remap = new String[]{ - type.toLowerCase(Locale.ENGLISH) - }; - } - - for (int counter = 0;; counter++) { - for (String subMap : remap) { - String attempt = subMap + ((counter == 0 && !subMap.equals("short") && (remap.length > 1 || subMap.length() > 1)) ? "" : counter); - if (!used.contains(attempt)) { - used.add(attempt); - return attempt; - } - } - } - } -} diff --git a/src/org/spigotmc/fernflower/EclipseFormatter.java b/src/org/spigotmc/fernflower/EclipseFormatter.java deleted file mode 100644 index ecd794c..0000000 --- a/src/org/spigotmc/fernflower/EclipseFormatter.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.spigotmc.fernflower; - -import org.eclipse.jdt.core.formatter.CodeFormatter; -import org.eclipse.jdt.internal.core.util.SimpleDocument; -import org.eclipse.jdt.internal.formatter.DefaultCodeFormatter; -import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions; -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.jface.text.IDocument; -import org.eclipse.text.edits.TextEdit; - -public class EclipseFormatter { - - private static final CodeFormatter formatter; - - static { - DefaultCodeFormatterOptions options = new DefaultCodeFormatterOptions(null); - options.setJavaConventionsSettings(); - - options.tab_char = DefaultCodeFormatterOptions.SPACE; - options.page_width = 0; - - options.insert_new_line_after_label = true; - options.insert_new_line_in_empty_method_body = false; - options.insert_new_line_in_empty_type_declaration = false; - - options.insert_space_before_closing_brace_in_array_initializer = false; // Compatability - // options.blank_lines_before_first_class_body_declaration = 1; // Needed later - - formatter = new DefaultCodeFormatter(options); - } - - public static String format(String contents) throws BadLocationException { - TextEdit formatted = formatter.format(CodeFormatter.K_COMPILATION_UNIT, contents, 0, contents.length(), 0, "\n"); - - IDocument doc = new SimpleDocument(contents); - formatted.apply(doc); - - return doc.get(); - } -}